AMD和CMD

以代碼愛好者角度來看AMD與CMD

隨著瀏覽器功能越來越完善腌且,前端已經(jīng)不僅僅是切圖做網(wǎng)站突颊,前端在某些方面已經(jīng)媲美桌面應(yīng)用。越來越龐大的前端項(xiàng)目光涂,越來越復(fù)雜的代碼,前端開發(fā)者們對于模塊化的需求空前強(qiáng)烈拧烦。后來node出現(xiàn)了忘闻,跟隨node出現(xiàn)的還有commonjs,這是一種js模塊化解決方案恋博,像Node.js主要用于服務(wù)器的編程齐佳,加載的模塊文件一般都已經(jīng)存在本地硬盤,所以加載起來比較快债沮,不用考慮異步加載的方式炼吴,CommonJS 加載模塊是同步的,所以只有加載完成才能執(zhí)行后面的操作疫衩。但是瀏覽器環(huán)境不同于Node硅蹦,瀏覽器中獲取一個資源必須要發(fā)送http請求,從服務(wù)器端獲取隧土,采用同步模式必然會阻塞瀏覽器進(jìn)程出現(xiàn)假死現(xiàn)象提针。在這方面dojo曾經(jīng)做了偉大嘗試,早期dojo便是采用xhr+eval的方式曹傀,結(jié)果可想而知辐脖,阻塞現(xiàn)象是必然的。后來出現(xiàn)無阻塞加載腳本方式在開發(fā)中廣泛應(yīng)用皆愉,在此基礎(chǔ)結(jié)合commonjs規(guī)范嗜价,前端模塊化迎來了兩種方案:AMD、CMD.

借用三藏法師一句話:人是人他媽生的幕庐,妖是妖他媽生的久锥。此話雖不雅,但用這里卻頗為貼切异剥。AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出瑟由,CMD是SeaJS 在推廣過程中被廣泛認(rèn)知。RequireJs出自dojo加載器的作者James Burke冤寿,SeaJs出自國內(nèi)前端大師玉伯歹苦。二者的區(qū)別青伤,玉伯在12年如是說

RequireJS 和 SeaJS 都是很不錯的模塊加載器,兩者區(qū)別如下:1. 兩者定位有差異殴瘦。RequireJS 想成為瀏覽器端的模塊加載器狠角,同時也想成為 Rhino /Node 等環(huán)境的模塊加載器。SeaJS 則專注于 Web 瀏覽器端蚪腋,同時通過 Node 擴(kuò)展的方式可以很方便跑在 Node 服務(wù)器端2. 兩者遵循的標(biāo)準(zhǔn)有差異丰歌。RequireJS 遵循的是 AMD(異步模塊定義)規(guī)范,SeaJS 遵循的是 CMD (通用模塊定義)規(guī)范屉凯。規(guī)范的不同立帖,導(dǎo)致了兩者 API 的不同。SeaJS 更簡潔優(yōu)雅神得,更貼近 CommonJS Modules/1.1 和 Node Modules 規(guī)范厘惦。3. 兩者社區(qū)理念有差異。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS哩簿,目前只有少數(shù)社區(qū)采納宵蕉。SeaJS 不強(qiáng)推,而采用自主封裝的方式來“海納百川”节榜,目前已有較成熟的封裝策略羡玛。4. 兩者代碼質(zhì)量有差異。RequireJS 是沒有明顯的 bug宗苍,SeaJS 是明顯沒有 bug稼稿。5. 兩者對調(diào)試等的支持有差異。SeaJS 通過插件讳窟,可以實(shí)現(xiàn) Fiddler 中自動映射的功能让歼,還可以實(shí)現(xiàn)自動 combo 等功能,非常方便便捷丽啡。RequireJS 無這方面的支持谋右。6. 兩者的插件機(jī)制有差異。RequireJS 采取的是在源碼中預(yù)留接口的形式补箍,源碼中留有為插件而寫的代碼改执。SeaJS 采取的插件機(jī)制則與 Node 的方式一致:開放自身,讓插件開發(fā)者可直接訪問或修改坑雅,從而非常靈活辈挂,可以實(shí)現(xiàn)各種類型的插件。.

關(guān)于二者的區(qū)別裹粤,前人之述備矣:

關(guān)于 CommonJS AMD CMD UMD

讓我們再聊聊瀏覽器資源加載優(yōu)化

SeaJS與RequireJS最大的區(qū)別

YUI Modules 與 AMD/CMD终蒂,哪一種方式更好?

JavaSript模塊規(guī)范 - AMD規(guī)范與CMD規(guī)范介紹

而在本文,我們僅從代碼愛好者的角度來一窺二者API后豫、模塊管理悉尾、加載、執(zhí)行的異同挫酿。

對比AMD與CMD規(guī)范,二者最大的區(qū)別在于依賴模塊的執(zhí)行時期愕难,CMD規(guī)范中明確要求延遲執(zhí)行(Execution must be lazy.)早龟。這一點(diǎn)從二者在模塊的定義方法define的函數(shù)簽名上可以看出:

AMD中define如下定義:

define(id?, dependencies?, factory);

id:String類型,它指定了模塊被定義時的id猫缭;可選的葱弟,如果省略,模塊id默認(rèn)使用加載器請求的響應(yīng)腳本的模塊id猜丹。

dependencies是一個模塊定義時要求的依賴項(xiàng)的模塊id數(shù)組字面量芝加。這些依賴項(xiàng)必須在factory方法執(zhí)行前被解析,解析值應(yīng)當(dāng)被當(dāng)做參數(shù)傳遞給factory函數(shù)射窒;factory的參數(shù)位置符合模塊在依賴項(xiàng)中的索引藏杖。

factory,是一個被用來執(zhí)行模塊初始化的參數(shù)或者是一個對象脉顿。如果factory是一個函數(shù)蝌麸,它應(yīng)當(dāng)只能被用來執(zhí)行一次。如果factory參數(shù)是一個對象艾疟,這個對象唄用來作為模塊的輸出值来吩。如果factory函數(shù)返回一個值(對象、函數(shù)蔽莱、任何可以被強(qiáng)制轉(zhuǎn)換為true的值)弟疆,這個值將會被作為模塊的輸出值。

define(["./a", "./b"],function(a, b) {//BEGINa.doSomething();

b.doSomething();

});

CMD中模塊如下定義:

define(function(require, exports, module) {

// The module code goes here

});

一個模塊使用define函數(shù)來定義

define函數(shù)只接受一個模塊工廠作為參數(shù)

factory必須是一個函數(shù)或者其他有效值

如果factory是一個函數(shù)盗冷,如果指定參數(shù)的話怠苔,前三個必須是“require”,“exports”正塌,“module”

如果factory不是一個函數(shù)嘀略,那么模塊的exports屬性被設(shè)置為那個有效對象

define(function(require, exports, module) {//BEGINrequire("./a").doSomething();

require("./b").doSomething();

});

需要提一下的是二者對待依賴模塊的加載是一致的沦辙,在factory執(zhí)行時呐萨,依賴模塊都已被加載。從代碼上來看洋腮,AMD中在BEGIN處a鸠天、b的factory都是執(zhí)行過的讼育;而CMD中雖然a、b模塊在BEGIN已被加載,但尚未執(zhí)行奶段,需要調(diào)用require執(zhí)行依賴模塊饥瓷。這就是CMD中著重強(qiáng)調(diào)的延遲執(zhí)行。如果這個例子不明顯的話痹籍,我們來看一下條件依賴:

AMD:

define(["./a", "./b"],function(a, b) {//BEGINif(true) {

a.doSomething();

}else{

b.doSomething();

}//END});

define(function(require) {//BEGINif(some_condition) {

require('./a').doSomething();

}else{

require('./b').soSomething();

}//END});

條件依賴意思是我們根據(jù)條件使用依賴項(xiàng)呢铆,在AMD中BEGIN位置處a、b模塊都需要被執(zhí)行一次蹲缠。CMD中BEGIN處a棺克、b都沒有被執(zhí)行,在END處线定,a娜谊、b只有一個被實(shí)際執(zhí)行過。

那么問題來了斤讥,javascript作為腳本語言纱皆,代碼肯定是順序執(zhí)行的,作為AMD與CMD的實(shí)現(xiàn)者芭商,requireJs與seaJs是如何知道需要加載的所有文件呢派草?又是如何做到異步加載?對于seajs蓉坎,factory中代碼肯定是順序執(zhí)行的澳眷,但是這必須導(dǎo)致require時的阻塞加載,而她又是如何保證異步加載的蛉艾?

每一個卓越的思想都有一份樸實(shí)的代碼實(shí)現(xiàn)钳踊。所以無論AMD與CMD都要面臨以下幾個問題:

1、模塊式如何注冊的勿侯,define函數(shù)都做了什么拓瞪?

2、他們是如何知道模塊的依賴助琐?

3祭埂、如何做到異步加載?尤其是seajs如何做到異步加載延遲執(zhí)行的兵钮?

辯證法第一規(guī)律:事物之間具有有機(jī)聯(lián)系蛆橡。AMD與CMD都借鑒了CommonJs,宏觀層面必有一致性掘譬,比如整體處理流程:

模塊的加載解析到執(zhí)行過程一共經(jīng)歷了6個步驟:

1泰演、由入口進(jìn)入程序

2、進(jìn)入程序后首先要做的就是建立一個模塊倉庫(這是防止重復(fù)加載模塊的關(guān)鍵)葱轩,JavaScript原生的object對象最為適合睦焕,key代表模塊Id藐握,value代表各個模塊,處理主模塊

3垃喊、向模塊倉庫注冊一模塊猾普,一個模塊最少包含四個屬性:id(唯一標(biāo)識符)、deps(依賴項(xiàng)的id數(shù)組)本谜、factory(模塊自身代碼)初家、status(模塊的狀態(tài):未加載、已加載未執(zhí)行耕突、已執(zhí)行等)笤成,放到代碼中當(dāng)然還是object最合適

4、模塊即是JavaScript文件眷茁,使用無阻塞方式(動態(tài)創(chuàng)建script標(biāo)簽)加載模塊

scriptElement= document.createElement('script');

scriptElement.src=moduleUrl;

scriptElement.async=true;

scriptElement.onload=function(){.........};

document.head.appendChild(scriptElement);

5、模塊加載完畢后纵诞,獲取依賴項(xiàng)(amd上祈、cmd區(qū)別),改變模塊status浙芙,由statuschange后登刺,檢測所有模塊的依賴項(xiàng)。

由于requirejs與seajs遵循規(guī)范不同嗡呼,requirejs在define函數(shù)中可以很容易獲得當(dāng)前模塊依賴項(xiàng)纸俭。而seajs中不需要依賴聲明,所以必須做一些特殊處理才能否獲得依賴項(xiàng)南窗。方法將factory作toString處理揍很,然后用正則匹配出其中的依賴項(xiàng),比如出現(xiàn)require(./a)万伤,則檢測到需要依賴a模塊窒悔。

同時滿足非阻塞和順序執(zhí)行就需要需要對代碼進(jìn)行一些預(yù)處理,這是由于CMD規(guī)范和瀏覽器環(huán)境特點(diǎn)所決定的敌买。

6简珠、如果模塊的依賴項(xiàng)完全加載完畢(amd中需要執(zhí)行完畢,cmd中只需要文件加載完畢虹钮,注意這時候的factory尚未執(zhí)行聋庵,當(dāng)使用require請求該模塊時,factory才會執(zhí)行芙粱,所以在性能上seajs遜于requirejs)祭玉,執(zhí)行主模塊的factory函數(shù);否則進(jìn)入步驟3.

最后宅倒,無論requireJs還是seaJs都已被廣泛應(yīng)用于web開發(fā)中攘宙,實(shí)際選取時應(yīng)根據(jù)以下幾方面綜合平衡選韧退省:

1、功能能否滿足項(xiàng)目需求

2蹭劈、文檔疗绣、demo的詳盡程度

3、框架的學(xué)習(xí)曲線

4铺韧、社區(qū)的活躍度

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末多矮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哈打,更是在濱河造成了極大的恐慌塔逃,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件料仗,死亡現(xiàn)場離奇詭異湾盗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)立轧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門格粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人氛改,你說我怎么就攤上這事帐萎。” “怎么了胜卤?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵疆导,是天一觀的道長。 經(jīng)常有香客問我葛躏,道長澈段,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任紫新,我火速辦了婚禮均蜜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芒率。我一直安慰自己囤耳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布偶芍。 她就那樣靜靜地躺著充择,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匪蟀。 梳的紋絲不亂的頭發(fā)上椎麦,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音材彪,去河邊找鬼观挎。 笑死琴儿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘁捷。 我是一名探鬼主播造成,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雄嚣!你這毒婦竟也來了晒屎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤缓升,失蹤者是張志新(化名)和其女友劉穎鼓鲁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體港谊,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骇吭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了歧寺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绵跷。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖成福,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荆残,我是刑警寧澤奴艾,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站内斯,受9級特大地震影響蕴潦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俘闯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一潭苞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧真朗,春花似錦此疹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旗扑,卻和暖如春蹦骑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背臀防。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工眠菇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留边败,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓捎废,卻偏偏與公主長得像笑窜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缕坎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容