(续上节)
上面讲了如何处理全局序列和context化全局序列
以及执行全局序列中的依赖项。
检查依赖项
//Mark all the dependencies as needing to be loaded.
context.nextTick(function() {
//Some defines could have been added since the
//require call, collect them.
intakeDefines();
requireMod = getModule(makeModuleMap(null, relMap));
//Store if map config should be applied to this require
//call for dependencies.
requireMod.skipMap = options.skipMap;
requireMod.init(deps, callback, errback, {
enabled: true
});
checkLoaded();
});
最后返回的是 return localRequire;
回到 context.require(deps, callback, errback);
4. 监听页面加载
传入的参数是load加载事件
脚本加载的回调,用于检查加载状态。
onScriptLoad: function(evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
if (evt.type === 'load' ||
(readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
//Reset interactive script so a script node is not held onto for
//to long.
interactiveScript = null;
//Pull out the name of the module and the context.
var data = getScriptData(evt);
context.completeLoad(data.id);
}
var data = getScriptData(evt) 这个函数 构造完毕之后,将返回一个对象
此时data就是{ node:script对象,id : ‘main’ }
5.获取加载对象
开始加载
completeLoad: function(moduleName) {
var found, args, mod,
shim = getOwn(config.shim, moduleName) || {},
shExports = shim.exports;
takeGlobalQueue();
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
args[0] = moduleName;
//If already found an anonymous module and bound it
//to this name, then this is some other anon module
//waiting for its completeLoad to fire.
if (found) {
break;
}
found = true;
} else if (args[0] === moduleName) {
//Found matching define call for this script!
found = true;
}
callGetModule(args);
}
context.defQueueMap = {};
//Do this after the cycle of callGetModule in case the result
//of those calls/init calls changes the registry.
mod = getOwn(registry, moduleName);
运行到这里,callGetModule()就是调用module了吧
function callGetModule(args) {
//Skip modules already defined.
if (!hasProp(defined, args[0])) {
getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
}
}
传入的args :args = [“main”, Array[0], undefined]
args[1]和args[2]到底是来干嘛的?
5.1 创造ModuleMap对象
首先makeModuleMap(args[0], null, true)).init(args[1], args[2]
从下图可以看到args[1]是当前加载项的父节点, args[2]->applyMap?:
转化为绝对路径
最后将main.js转化为url路径,可能在这里没什么用,但是在这种定义了baseURL或者重命名模块中,这个normolize显得非常有用的
最后得到的对象:
5.2 getModule
5.3 Module.init
5.4 checkLoaded()
checkLoaded()会调用onScriptLoad(),出现了新的./@r5.js 是a.js吗
然而不是a.js,第二次遍历加载出了a.js
又见到熟悉的@r5.js,这次它变成了Module
6 加载第一个script:a.js
req.load = function(context, moduleName, url) {
var config = (context && context.config) || {},
node;
if (isBrowser) {
//In the browser so use a script tag
node = req.createNode(config, moduleName, url);
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
6.1 检测是否是浏览器
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
6.2 创建node
req.createNode = function(config, moduleName, url) {
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
return node;
};
传入的参数:
这个path是在main.js中定义的
require.config({
path: {
"a": "a",
"b": "b"
}
})
deps=[‘main’],应该main默认就为依赖项吧,因为之前在
createNode()就是构造一段script,而且指定了charset-set 和async(异步加载);
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
这两句话为node添加了新的属性
为node绑定新的事件
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);
加载事件和error事件都绑定为context的对象了
其实node就是script,但是require这里为它添加了两个新的属性和新的load处理事件,我觉得requireJS的精妙之处就在这里吧。
继续循环看是否有加载项
这个时候a.js并没有加载,又出现了.@r5,我猜测b.js要出来了
然而我们看到的是,removeScript(“a”),要移除a了?但是a都没有加载啊
不对,这里是要加载a.js,然而报错了,应该是因为我们debug超出了requireJS的时间。
.@r6 .@r8出现了
先写到这里,requireJS的原理确切是很复杂,经常会对一些全局变量进行修改啥的。
到这里onScriptLoad()就结束了