例子來自官方,我稍微改造了一下,如下:
// 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 這時會在事件處理函數中進行檢測,加載的模塊是否同時也存在依賴?來看一段代碼:
/**
* 這個方法是在 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;
}
}
本文僅作了解,更多的內容我會單獨寫文章講解
