用一個例子讀懂 RequireJS
阿新 • • 發佈:2017-06-14
[0 src navigator resume www attribute base alert var
用一個例子讀懂 RequireJS
例子來自官方,我稍微改造了一下,如下:
// project.html
<! DOCTYPE html>
< html >
< head >
< title >requirejs</ title >
<!-- data-main attribute tells require.js to load
scripts/main.js after require.js loads. -->
< script data-main="scripts/main" src="scripts/require.js"></ script >
</ head >
< body >
</ body >
</ html >
// scripts/main.js
define(function(util){
require("helper/util");
alert(‘main factory‘)
return {a:1, b:2};
})
// scripts/helper/util.js
alert(‘util.js is loaded!‘) |
三個文件,運行結果 "util.js is loaded" -> "main factory"。
data-main 屬性有一個值 "scripts/main",表示當 require.js 加載完之後加載 scripts/main.js。
其實不僅如此,如果指定了這個屬性,會把它的目錄部分和文件部分拆開,即 scripts/ 和 main,之後會這麽做:
cfg.baseUrl = ‘scripts/‘;
cfg.deps = [‘main‘];
即配置一下 baseUrl 和 deps
接著,用這個配置對象去初始化默認Context,這個過程會判斷默認Context 是否依賴別的模塊,這裏明顯依賴 "main",所以需要context.require(it)
1. 把需要加載的依賴放進一個數組
2. 遍歷該數組,加載依賴,並輪詢加載狀態
append 的 script 節點需要提一下:
可以看到 RequireJS 為節點加了兩個自定義屬性,分別表示 contextName 和 moduleName
動態創建的 script 節點當腳本執行完後,會發出onload事件(IE 就是 onreadstatechange),RequireJS 這時會在事件處理函數中進行檢測,加載的模塊是否同時也存在依賴?來看一段代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/**
* 這個方法是在 script 節點加載的腳本執行完之後才會執行。
* 1. 通過 event.target 或 event.srcElement 可以取到 script 節點
* 2. 獲取節點的 data-requiremodule 屬性時,如這裏的 "main"
* 所以 moduleName 就是 "main"
*
* @param {String} moduleName
*/
completeLoad: function (moduleName) {
var args;
// 隊列分為 全局隊列 和 context隊列
//
// 一個被加載的模塊,在它的define()中,會把該模塊對應的[name, deps, callback]塞進隊列
// 如果遵循一個文件一個模塊的寫法,隊列裏只有一個元素
// 如果一個文件寫了多個模塊,那隊列裏有多個元素
//
// 現在的問題是:到底塞進哪個隊列呢?
// 因為 IE 通過 interactive 狀態可以知道當前執行的 script 節點,
// 而 script 節點又綁定了 data-requirecontext 屬性,所以可以拿到contextName
// 綜上:IE 加入 context隊列,非IE加入 全局隊列
//
// 這句就表示把全局隊列的元素加入context隊列,並清空全局隊列
// 這樣便實現了瀏覽器的兼容
context.takeGlobalQueue();
// defQueue 即 context 隊列
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null ) {
// 如果[name, deps, callback]中name為null,即匿名模塊
args[0] = moduleName;
break ;
} else if (args[0] === moduleName) {
//Found matching define call for this script!
break ;
} else {
// 如果一個文件出現多個define,才有可能進到這裏,暫時可以無視這個分支
callDefMain(args);
args = null ;
}
}
// callDefMain其實是main()的apply調用
// 它是定義模塊的主函數,通過[name, deps, callback]構造模塊
// 它會獲取模塊需要的依賴,如果是未加載的依賴,會加入context.paused數組
if (args) {
callDefMain(args);
} else {
// 如果加載的文件沒有寫成模塊的形式,進到這裏
callDefMain([moduleName, [], null ]);
}
// 每加載完一個,context.scriptCount就-1
// 對瀏覽器來說,這沒什麽問題,但這有一個副作用
// checkLoaded() 會通過scriptCount判斷是否要輪詢加載狀態
// 為了避免這個開銷, 這裏先-1
if (req.isAsync) {
context.scriptCount -= 1;
}
// 這個方法主要就是處理context.paused,即加載那些依賴
// 並會輪詢是否完成加載,並在加載完成時,做一些事
resume();
if (!req.isAsync) {
context.scriptCount -= 1;
}
}
|
用一個例子讀懂 RequireJS