用一個例子讀懂 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 這時會在事件處理函數中進行檢測,加載的模塊是否同時也存在依賴?來看一段代碼:

/**
 * 這個方法是在 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;
	}
}


本文僅作了解,更多的內容我會單獨寫文章講解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM