1. 程式人生 > >用一個例子讀懂 RequireJS

用一個例子讀懂 RequireJS

[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