JS模塊化規範CommonJS,AMD,CMD

模塊化是軟件系統的屬性,這個系統被分解爲一組高內聚,低耦合的模塊。理想狀態下咱們只須要完成本身部分的核心業務邏輯代碼,其餘方面的依賴能夠經過直接加載被人已經寫好模塊進行使用便可。
一個模塊化系統所必須的能力: javascript

 
 
 
 
 
定義封裝的模塊。 定義新模塊對其餘模塊的依賴。 可對其餘模塊的引入支持。

CommonJS
nodeJs出現後使用了CommonJS規範來解決JS的模塊化問題。因爲Node.js主要用於服務器的編程,加載的模塊文件通常都已經存在本地硬盤,因此加載起來比較快,不用考慮異步加載的方式,因此CommonJS規範也是同步加載依賴模塊,加載完後執行後面代碼。但若是是瀏覽器環境,要從服務器加載模塊,這是就必須採用異步模式。因此就有了 AMD CMD 解決方案。html

AMD和CMD前端

AMD(Asynchromous Module Definition)是equireJS 在推廣過程當中對模塊定義的規範化產出。CMD是SeaJS 在推廣過程當中對模塊定義的規範化產出。RequireJs出自dojo加載器的做者James Burke,SeaJs出自國內前端大師玉伯。兩者的區別,玉伯在12年如是說java

RequireJS 和 SeaJS 都是很不錯的模塊加載器,二者區別以下:
二者定位有差別。RequireJS 想成爲瀏覽器端的模塊加載器,同時也想成爲 Rhino / Node 等環境的模塊加載器。SeaJS 則專一於 Web 瀏覽器端,同時經過 Node 擴展的方式能夠很方便跑在 Node 服務器端.
二者遵循的標準有差別。RequireJS 遵循的是 AMD(異步模塊定義)規範,SeaJS 遵循的是 CMD (通用模塊定義)規範。規範的不一樣,致使了二者 API 的不一樣。SeaJS 更簡潔優雅,更貼近 CommonJS Modules/1.1 和 Node Modules 規範。
二者社區理念有差別。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS,目前只有少數社區採納。SeaJS 不強推,而採用自主封裝的方式來「海納百川」,目前已有較成熟的封裝策略。
二者代碼質量有差別。RequireJS 是沒有明顯的 bug,SeaJS 是明顯沒有 bug。
二者對調試等的支持有差別。SeaJS 經過插件,能夠實現 Fiddler 中自動映射的功能,還能夠實現自動 combo 等功能,很是方便便捷。RequireJS 無這方面的支持。
二者的插件機制有差別。RequireJS 採起的是在源碼中預留接口的形式,源碼中留有爲插件而寫的代碼。SeaJS 採起的插件機制則與 Node 的方式一致:開放自身,讓插件開發者可直接訪問或修改,從而很是靈活,能夠實現各類類型的插件。.

其中反對這麼作的人的 觀點

我我的感受requirejs更科學,全部依賴的模塊要先執行好。若是A模塊依賴B。當執行A中的某個操doSomething()後,再去依賴執行B模塊require('B');若是B模塊出錯了,doSomething的操做如何回滾? 不少語言中的import, include, useing都是先將導入的類或者模塊執行好。若是被導入的模塊都有問題,有錯誤,執行當前模塊有何意義?node

而依賴dependencies是工廠的原材料,在工廠進行生產的時候,是先把原材料一次性都在它本身的工廠里加工好,仍是把原材料的工廠搬到當前的factory來何時須要,何時加工,哪一個總體時間效率更高?jquery

首先回答第一個問題。git

第一個問題的題設並不徹底正確,「依賴」和「執行」的概念比較模糊。編程語言執行一般分爲兩個階段,編譯(compilation)和運行(runtime)。對於靜態語言(好比C/C++)來講,在編譯時若是出現錯誤,那可能以前的編譯都視爲無效,的確會出現描述中須要回滾或者從新編譯的問題。但對於動態語言或者腳本語言,大部分執行都處在運行時階段或者解釋器中:假設我使用Nodejs或者Python寫了一段服務器運行腳本,在持續運行了一段時間以後由於某項需求要加載某個(依賴)模塊,同時也由於這個模塊致使服務端掛了——我認爲這時並不存在回滾的問題。在加載依賴模塊以前當前的模塊的大部分功能已經成功運行了。github

再回答第二個問題。web

對於「工廠」和「原材料」的比喻不夠恰當。難道依賴模塊沒有加載完畢當前模塊就沒法工做嗎?requirejs的確是這樣的,從上面的截圖能夠看出,依賴模塊老是先於當前模塊加載和執行完畢。但咱們考慮一下基於CommonJS標準的Nodejs的語法,使用require函數加載依賴模塊能夠在頁面的任何位置,能夠只是在須要的時候。也就是說當前模塊沒必要在依賴模塊加載完畢後才執行。編程

你可能會問,爲何要拿AMD標準與CommonJS標準比較,而不是CMD標準?

玉伯在CommonJS 是什麼這篇文章中已經告訴了咱們CMD某種程度上遵循的就是CommonJS標準:

從上面能夠看出,Sea.js 的初衷是爲了讓 CommonJS Modules/1.1 的模塊能運行在瀏覽器端,但因爲瀏覽器和服務器的實質差別,實際上這個夢沒法徹底達成,也沒有必要去達成。

更好的一種方式是,Sea.js 專一於 Web 瀏覽器端,CommonJS 則專一於服務器端,但二者有共通的部分。對於須要在兩端均可以跑的模塊,能夠 有便捷的方案來快速遷移。

CMD推崇依賴就近,能夠把依賴寫進你的代碼中的任意一行,例:

 
 
 
 
 
define(function(require, exports, module) { var a = require('./a') a.doSomething() var b = require('./b') b.doSomething()})

代碼在運行時,首先是不知道依賴的,須要遍歷全部的require關鍵字,找出後面的依賴。具體作法是將function toString後,用正則匹配出require關鍵字後面的依賴。顯然,這是一種犧牲性能來換取更多開發便利的方法。

而AMD是依賴前置的,換句話說,在解析和執行當前模塊以前,模塊做者必須指明當前模塊所依賴的模塊,表如今require函數的調用結構上爲:

 
 
 
 
 
define(['./a','./b'],function(a,b){ a.doSomething() b.doSomething()})

代碼在一旦運行到此處,能當即知曉依賴。而無需遍歷整個函數體找到它的依賴,所以性能有所提高,缺點就是開發者必須顯式得指明依賴——這會使得開發工做量變大,好比:當你寫到函數體內部幾百上千行的時候,突然發現須要增長一個依賴,你不得不回到函數頂端來將這個依賴添加進數組。

細心的讀者可能發現,到目前位置我討論的AMD和CMD的思想的關於依賴的部分,都只討論的「硬依賴」,也就是執行前確定須要的依賴,可是這不是所有的狀況。有的時候狀況是這樣的:

 
 
 
 
 
// 函數體內:if(status){ a.doSomething()}

在這個函數體內,可能依賴a,也可能不依賴a,我把這種可能的依賴成爲「軟依賴」。對於軟依賴固然能夠直接當硬依賴處理,可是這樣不經濟,由於依賴是不必定的,有可能加載了此處的依賴而實際上沒有用上。
對於軟依賴的處理,我推薦依賴前置+回調函數的實現形式。上面的例子簡單表述以下:

 
 
 
 
 
// 函數體內:if(status){ async(['a'],function(a){ a.doSomething() })}

至此能夠對由commonJS衍生出來的方案作出總結了。在瀏覽器端來設計模塊加載機制,須要考慮依賴的問題。
咱們先把依賴分爲兩種,「強依賴」 —— 確定須要 和「弱依賴」 —— 可能須要。
對於強依賴,若是要性能優先,則考慮參照依賴前置的思想設計你的模塊加載器;若是考慮開發成本優先,則考慮按照依賴就近的思想設計你的模塊加載器。
對於弱依賴,只須要將弱依賴的部分改寫到回調函數內便可。
不管AMDCMD都要面臨如下幾個問題:

  一、模塊式如何註冊的,define函數都作了什麼?
  二、他們是如何知道模塊的依賴?
  三、如何作到異步加載?尤爲是seajs如何作到異步加載延遲執行的?
  辯證法第一規律:事物之間具備有機聯繫。AMDCMD都借鑑了CommonJs,宏觀層面必有一致性,好比總體處理流程:

模塊的加載解析到執行過程一共經歷了6個步驟:

  一、由入口進入程序

  二、進入程序後首先要作的就是創建一個模塊倉庫(這是防止重複加載模塊的關鍵),JavaScript原生的object對象最爲適合,key表明模塊Id,value表明各個模塊,處理主模塊

  三、向模塊倉庫註冊一模塊,一個模塊最少包含四個屬性:id(惟一標識符)、deps(依賴項的id數組)、factory(模塊自身代碼)、status(模塊的狀態:未加載、已加載未執行、已執行等),放到代碼中固然仍是object最合適

  四、模塊便是JavaScript文件,使用無阻塞方式(動態建立script標籤)加載模塊
五、模塊加載完畢後,獲取依賴項(amd、cmd區別),改變模塊status,由statuschange後,檢測全部模塊的依賴項。

  因爲requirejs與seajs遵循規範不一樣,requirejs在define函數中能夠很容易得到當前模塊依賴項。而seajs中不須要依賴聲明,因此必須作一些特殊處理纔可否得到依賴項。方法將factory做toString處理,而後用正則匹配出其中的依賴項,好比出現require(./a),則檢測到須要依賴a模塊。

 六、若是模塊的依賴項徹底加載完畢(amd中須要執行完畢,cmd中只須要文件加載完畢,注意這時候的factory還沒有執行,當使用require請求該模塊時,factory纔會執行,因此在性能上seajs遜於requirejs),執行主模塊的factory函數;不然進入步驟3。

處理流程摘自:以代碼愛好者角度來看AMDCMD
寫法舉例
CommonJs

 
 
 
 
 
// 文件名: foo.jsdefine(['jquery', 'underscore'], function ($, _) {// 方法function a(){}; // 私有方法,由於沒有被返回(見下面)function b(){}; // 公共方法,由於被返回了function c(){}; // 公共方法,由於被返回了     //    暴露公共方法    return {        b: b,        c: c    }});```AMD```javascript//    文件名: foo.jsvar $ = require('jquery');var _ = require('underscore');// methodsfunction a(){}; // 私有方法,由於它沒在module.exports中 (見下面)function b(){}; // 公共方法,由於它在module.exports中定義了function c(){}; // 公共方法,由於它在module.exports中定義了// 暴露公共方法module.exports = { b: b, c: c};

CMD

 
 
 
 
 
define(function (requie, exports, module) { //依賴能夠就近書寫 var a = require('./a'); a.test(); ... //軟依賴 if (status) { var b = requie('./b'); b.test(); }});

UMD
UMD是AMD和CommonJS的糅合,兼容了AMD和CommonJS,同時還支持老式的「全局」變量規範。
AMD 瀏覽器第一的原則發展 異步加載模塊。CommonJS 模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝(unwrapped modules)。這迫令人們又想出另外一個更通用的模式UMD (Universal Module Definition)。但願解決跨平臺的解決方案。UMD先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式。
在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。

 
 
 
 
 
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery', 'underscore'], factory); } else if (typeof exports === 'object') { // Node, CommonJS之類的 module.exports = factory(require('jquery'), require('underscore')); } else { // 瀏覽器全局變量(root 即 window) root.returnExports = factory(root.jQuery, root._); }}(this, function ($, _) { // 方法 function a(){}; // 私有方法,由於它沒被返回 (見下面) function b(){}; // 公共方法,由於被返回了 function c(){}; // 公共方法,由於被返回了 // 暴露公共方法 return { b: b, c: c }}));

AMD、CMD、UMD 模塊的寫法
關於 CommonJS AMD CMD UMD
擴展閱讀:
AMD規範文檔
amdjs 的 require 接口文檔
amdjs 的接口文檔
RequireJS官網接口文檔
模塊系統
前端模塊化開發的價值
前端模塊化開發那點歷史
CMD 模塊定義規範
SeaJS API快速參考
從 CommonJS 到 Sea.js
RequireJS和AMD規範
CommonJS規範
Javascript模塊化編程
Javascript模塊化編程
知乎 AMD 和 CMD 的區別有哪些?
JavaScript模塊化開發 - CommonJS規範
JavaScript模塊化開發 - AMD規範
模塊化設計
模塊化



相關文章
相關標籤/搜索