| 1.牢騷CommonJS,  AMD, CMD , 其實(shí)很早就接觸過了。當(dāng)時(shí),  網(wǎng)上的文章看得眼花繚亂,  只依稀記得幾個(gè)模糊的概念。
 什么依賴前置 ,  什么按需加載。
 一頭霧水。
 現(xiàn)在再回過頭來看看概念 , 網(wǎng)上部分文章用詞模棱兩可。給我們這些菜雞,  帶來了理解的偏差和困惑。
 記得第一個(gè)項(xiàng)目還用了requireJS 。
 時(shí)過境遷,現(xiàn)在入門前端 ,  都是直接上webpack了 。
 但我覺得還是有必要理一理 。
 
 2. 是什么CommonJS, AMD, CMD是規(guī)范, 理念 ; 對(duì) CommonJS 的實(shí)現(xiàn) , 有 node 的模塊系統(tǒng) ;對(duì) AMD 的實(shí)現(xiàn)有require.js ;而 CMD, 是在sea.js的實(shí)現(xiàn)中提出來的 (但是在Google和Stack Overflow, 這個(gè)概念很少被提到, 一般出現(xiàn)在國(guó)內(nèi))。
 
 CommonJS規(guī)范, 模塊加載是同步的
 對(duì)node來說,模塊存放在本地硬盤,同步加載,等待時(shí)間就是硬盤的讀取時(shí)間,這個(gè)時(shí)間非常短;AMD、CMD 規(guī)范,模塊加載是異步的
 目的, 是為了適應(yīng)瀏覽器環(huán)境,加載的時(shí)間取決于網(wǎng)絡(luò)的好壞,可能要等很長(zhǎng)時(shí)間;
 
 3. 先說 async 和 defer記得看文章的時(shí)候,  看到了下面這段話 ,
  我看了半天, 總覺得不是很理解 ,  為什么呢 ?
 這里說,
 腳本標(biāo)簽天生是異步的, 那為什么會(huì)出現(xiàn) async 和 defer ?然后,  我發(fā)覺我并沒有把
 加載和執(zhí)行的概念區(qū)分清楚 , 這里的加載我把它理解為瀏覽器中的下載這里貼一張圖就很清楚了:
 
  <script>標(biāo)簽, 在下載和執(zhí)行的時(shí)候 , 會(huì)阻塞dom的渲染進(jìn)程 , 所以如果把<script>標(biāo)簽放在<head>中, 當(dāng) js 文件很大或者網(wǎng)絡(luò)差時(shí), 會(huì)導(dǎo)致頁(yè)面長(zhǎng)時(shí)間空白( 順帶提一下, <script>標(biāo)簽并不會(huì)阻止其他的<script>標(biāo)簽的下載, 現(xiàn)代瀏覽器中多個(gè)<script>下載是并行的, 在chrome中, 默認(rèn)支持6個(gè)資源(http1.x)并行下載 ), 另外 , 腳本是按照<script>標(biāo)簽的書寫順序執(zhí)行的 ;
<script defer>在加上defer以后, 下載的過程就不會(huì)阻塞dom渲染了,  但腳本的執(zhí)行是在dom渲染完畢之后;
<script async>在加上async以后, 下載的過程同樣不會(huì)阻塞dom渲染,  但腳本會(huì)在下載完后立刻執(zhí)行, 所以存在多個(gè)<script async>時(shí), 無(wú)法保證多個(gè)js文件的執(zhí)行順序, 加載較快的腳本會(huì)執(zhí)行;
 所以defer, async主要作用于加載階段, 執(zhí)行階段仍然會(huì)阻塞dom渲染 
 4.  再看 require.js 的異步體現(xiàn)再看看使用require.js的模塊寫法新建 main.js / a.js / b.js ,  main.js 為入口, 引用了a.js ,  b.js
 	// main.js
	// waitSeconds = 0的配置, 是為了防止文件過大或網(wǎng)絡(luò)不佳時(shí), 加載時(shí)間過長(zhǎng)導(dǎo)致require報(bào)`Load timeout for modules`的錯(cuò)誤 
	require.config({
   		 waitSeconds: 0
  	});
	require(['a.js', 'b.js'], function(a, b){
		// handle / use a, b
		console.log(a)
		console.log(b)
	})
	
	// a.js   ------------------------------
	define([], function(){
		return {
			a: 111111111111
		}
	})
	
	// b.js   ------------------------------
	define([], function(){
		return {
			b: 222222222222
		}
	})	
 文件 開始下載 的 順序: main, a, b
 為什么文件下載的順序是 main, a, b 呢? main依賴了 a b, 不是 a b 先下載嗎? 那是因?yàn)?,只?main 加載之后,才知道m(xù)ian依賴了啥啊執(zhí)行的 順序 : a, b, main 或者 b, a,  main
 這里體現(xiàn) require.js 的異步加載。 a 和 b 的加載或者說下載是并行的, 但 a 和 b 的執(zhí)行順序不確定的 , a 和 b 先執(zhí)行哪一個(gè)都無(wú)所謂 ,只需要保證回調(diào)函數(shù)在 a 和 b 都執(zhí)行完之后再執(zhí)行就可以了;
 在require.js中模塊加載是怎么實(shí)現(xiàn)的呢?看一下require.js的源碼:
     /**
     * Creates the node for the load command. Only used in browser envs.
     */
    req.createNode = function (config, moduleName, url) {
        var node = config.xhtml ?
                document.createElementNS('http://www./1999/xhtml', 'html:script') :
                document.createElement('script');
        node.type = config.scriptType || 'text/javascript';
        node.charset = 'utf-8';
        node.async = true;   
        return node;
    };
 這段代碼, 新建了script標(biāo)簽, 并把它的 async設(shè)置為true, 另外, 前面說 , 依賴的模塊都執(zhí)行完之后,  才會(huì)執(zhí)行回調(diào)函數(shù)。  那怎么判斷是否 所有依賴的模塊 都已經(jīng)執(zhí)行完 ?多個(gè)模塊的情況 , 還沒看懂(捂臉) , 但是單個(gè)模塊的執(zhí)行狀態(tài)是可以監(jiān)聽的:
 	...
	...
	...
        //mentioned above about not doing the 'script execute,
        //then fire the script load event listener before execute
        //next script' that other browsers do.
        //Best hope: IE10 fixes the issues,
        //and then destroys all installs of IE 6-9.
        //node.attachEvent('onerror', context.onScriptError);
    } else {
        node.addEventListener('load', context.onScriptLoad, false);
        node.addEventListener('error', context.onScriptError, false);
    }
    node.src = url;
    ...
    ...
 上面的代碼可以看到,  通過 <script>標(biāo)簽的onload事件可以判斷, 該腳本是否執(zhí)行完畢 ; 所以, 個(gè)人理解, require.js的異步第一, 是指下載的異步,
 第二, 還指回調(diào)機(jī)制, 依賴模塊執(zhí)行完之后再執(zhí)行回調(diào)函數(shù)
 5.  AMD 和 CMD 的理解誤區(qū)現(xiàn)在 再來看 AMD 和 CMD 的區(qū)別,   網(wǎng)上的說法: AMD推崇依賴前置,在定義模塊的時(shí)候就要聲明其依賴的模塊CMD推崇就近依賴,只有在用到某個(gè)模塊的時(shí)候再去require
 第二點(diǎn) 只有在用到某個(gè)模塊的時(shí)候再去require, 這種說其實(shí)是帶有誤導(dǎo)性的,看看sea.js的寫法:
 	define(function(require, exports, module) {  
		console.log(123) 
	    var a = require('a.js')
	    console.log(a)
	    var b = require('b.js')
	    console.log(b)
	})
 這里,  難道是執(zhí)行到require,  才去加載/下載require的文件嗎 ?當(dāng)然不是 !  看一下sea.js的代碼:
 	window.define = function(callback) {
    	var id = getCurrentJs()
    	var depsInit = s.parseDependencies(callback.toString())
    	var a = depsInit.map(item => basepath   item)
    ....
 sea.js把callback回調(diào)函數(shù)用轉(zhuǎn)換成字符串, 再找出有哪些依賴, 這些依賴模塊同樣是預(yù)先加載的 ,不同在于,  require.js會(huì)立刻執(zhí)行依賴模塊,  而sea.js 在遇到
 require語(yǔ)句的時(shí)候 ,  再執(zhí)行依賴模塊; 5.  總結(jié)AMD和CMD最大的區(qū)別是:  對(duì)依賴模塊的執(zhí)行時(shí)機(jī)處理不同(注意不是加載的時(shí)機(jī))很多人說,  requireJS是異步加載模塊,SeaJS是同步加載模塊,這么說實(shí)際上是不準(zhǔn)確的 ;
 二者加載模塊都是異步的 ;
 只不過AMD依賴前置,可以方便知道依賴了哪些模塊,然后馬上加載 ,  在加載完成后,  就會(huì)執(zhí)行該模塊;
 而CMD推崇就近依賴,把模塊變?yōu)樽址馕鲆槐?  找到依賴了哪些模塊,  在加載模塊完成后,  不立刻執(zhí)行,  而是等到
 require后再執(zhí)行;上面只說了異步相關(guān)的概念,  其實(shí) require.js / sea.js ,  最重要的還是模塊化。
 模塊化降低耦合,依賴清晰,讓調(diào)試,  加功能,  任務(wù)分配和交接都更方便。
 |