(續上節)
上面講了如何處理全局序列和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()就結束了