CMD(<code>Common Module Definition)</code>表示通用子產品定義,該規範是國内發展出來的,由阿裡的玉伯提出。就像AMD有個requireJS,CMD有個浏覽器的實作SeaJS,SeaJS和requireJS一樣,都是javascript的子產品化解決方案。本文将詳細介紹CMD和seaJS
在Sea.js中,所有JavaScript子產品都遵循CMD(Common Module Definition)子產品定義規範。該規範明确了子產品的基本書寫格式和基本互動規則
AMD規範簡單到隻有一個API,即define函數
module-name: 子產品辨別,可以省略
array-of-dependencies: 所依賴的子產品,可以省略
module-factory-or-object: 子產品的實作,或者一個JavaScript對象
CMD規範也與之類似,隻不過第三個參數factory的實作方式不同。在CMD規範中,一個子產品就是一個檔案。代碼的書寫格式如下
與AMD規範類似,define是一個全局函數,用來定義子產品。字元串 <code>id</code> 表示子產品辨別,數組 <code>deps</code> 是子產品依賴。這兩個參數可以省略,通常由建構工具自動生成
通常地,define()方法的第三個參數factory是一個函數,表示是子產品的構造方法。執行該構造方法,可以得到子產品向外提供的接口。<code>factory</code> 方法在執行時,預設會傳入三個參數:<code>require</code>、<code>exports</code> 和 <code>module</code>
[注意]factory()方法的參數如果不需要,可以省略,但不可以修改,如修改為'a'、'b'、'c',也不可以改變其參數的順序。在函數内部,也不能對參數名重新指派,如'var a = require; '
【require】
<code> require</code> 是 <code>factory</code> 函數的第一個參數。<code>require</code> 是一個方法,接受 子產品辨別 作為唯一參數,用來擷取其他子產品提供的接口。通俗地說,通過require()方法來調用其他子產品的屬性或方法
這個require()方法的實作和功能都特别類似于CommonJS中的require()方法。或許,有人會有疑惑,require()不是一個同步方法嗎?在CommonJS中是的,在seaJS中也可以這麼說,但并不完整。更合理的說法應該是,子產品内的同步加載,實際表現為對子產品a進行預下載下傳
例如下面的代碼,即使不點選頁面,a.js也會預先下載下傳。點選頁面後,控制台依次輸出'a'和'a.test'
能不能執行時再下載下傳呢?類似于懶加載。有的,使用require.async()方法。<code>require.async</code> 方法用來在子產品内部異步加載子產品,并在加載完成後執行指定回調
【exports】
<code> exports</code> 是一個對象,用來向外提供子產品接口。與CommonJS的exports功能類似
除了給 <code>exports</code> 對象增加成員,還可以使用 <code>return</code> 直接向外提供接口,這種方式與requireJS的方式類似
如果 <code>return</code> 語句是子產品中的唯一代碼,還可簡化為
【module】
<code>module</code> 是一個對象,上面存儲了與目前子產品相關聯的一些屬性和方法

module.uri表示根據子產品系統的路徑解析規則得到的子產品絕對路徑
module.id是子產品的唯一辨別,一般情況下沒有在define中手寫id參數時,module.id的值就是module.uri,兩者完全相同
module.dependencies是一個數組,表示目前子產品的依賴
module.exports是目前子產品對外提供的接口。傳給factory構造方法的exports參數是module.exports對象的一個引用。隻通過exports參數來提供接口,有時無法滿足開發者的所有需求。 比如當子產品的接口是某個類的執行個體時,需要通過module.exports來實作
[注意]對<code>module.exports</code>的指派需要同步執行,不能放在回調函數裡。下面這樣是不行的
requireJS通過data-main來設定入口,而seaJS則通過sea.use()來設定。sea.js 在下載下傳完成後,會自動加載入口子產品
[注意]<code>callback</code>參數可選,省略時,表示無需回調
加載單個依賴,運作以下代碼後,控制台輸出'test'
加載多個依賴
【DOMReady】
<code> seajs.use</code>與<code>DOM ready</code>事件沒有任何關系。如果某些操作要確定在<code>DOM ready</code>後執行,需要使用<code>jquery</code>等類庫來保證
【打包】
引入 <code>sea.js</code> 時,可以把 <code>sea.js</code> 與其他檔案打包在一起,可提前合并好,或利用 combo 服務動态合并。無論哪一種方式,為了讓 <code>sea.js</code> 内部能快速擷取到自身路徑,推薦手動加上 <code>id</code> 屬性
加上 <code>seajsnode</code> 值,可以讓 <code>sea.js</code> 直接擷取到自身路徑,而不需要通過其他機制去自動擷取。這對性能和穩定性會有一定提升,推薦預設都加上
【路徑】
如果不配置路徑,在requireJS中,預設路徑是data-main的所處目錄,比如data-main='js/main',則所處路徑是'js'目錄下
而seaJS則不同,它的預設路徑是seaJS檔案的所處目錄,比如seaJS檔案所處路徑是'demo'目錄下,進行如下入口設定後
說明main.js的目錄為'demo/js/main.js'。如果main.js依賴于a.js,且a.js與main.js處于同一目錄下,則以下兩種寫法都正确
1、'demo' + 'js/a' = 'demo/js/a.js'
2、'./'表示目前目錄,即'demo/js',是以 './a' = 'demo/js/a.js'
在requireJS中使用baseUrl來配置基礎路徑,而在seaJS中使用base。進行如下配置後,真實路徑為 'demo' + 'js' + 'main' = 'demo/js/main.js'
【别名】
當子產品辨別很長時,可以使用 <code>alias</code> 來簡化
【目錄】
當目錄比較深,或需要跨目錄調用子產品時,可以使用 <code>paths</code> 來簡化書寫
AMD 是 RequireJS 在推廣過程中對子產品定義的規範化産出,CMD 是 SeaJS 在推廣過程中對子產品定義的規範化産出。這些規範的實作都能達成浏覽器端子產品化開發的目的
AMD與CMD主要有以下兩點差別
1、所依賴子產品的執行時機
對于依賴的子產品,AMD是提前執行,CMD是延遲執行
AMD在加載子產品完成後就會執行該子產品,所有子產品都加載執行完後會進入require的回調函數,執行主邏輯,這樣的效果就是依賴子產品的執行順序和書寫順序不一定一緻,看網絡速度,哪個先下載下傳下來,哪個先執行,但是主邏輯一定在所有依賴加載完成後才執行。不過,新版本的RequireJS也可以延遲執行
CMD加載完某個依賴子產品後并不執行,隻是下載下傳而已,在所有依賴子產品加載完成後進入主邏輯,遇到require語句的時候才執行對應的子產品,這樣子產品的執行順序和書寫順序是完全一緻的。如果使用require.async()方法,可以實作子產品的懶加載,即不執行不下載下傳
2、CMD推崇依賴就近,AMD推崇依賴前置
當然,AMD也支援CMD的寫法,同時還支援将require作為依賴項傳遞
CommonJS、requireJS、seaJS這三種子產品化方案,并沒有高低之分。随着各個方案的不斷更新,語言方面互相借鑒,使用差異逐漸變小。以上三種庫級别的子產品化方案,需要引入額外的庫,且所遵循的規範并不是标準組織制定的,權威性不足
随着ES6在語言層面上開始支援子產品化,ES6的子產品化寫法才是未來的子產品化标準
本文轉自xsster51CTO部落格,原文連結:http://blog.51cto.com/12945177/1948453 ,如需轉載請自行聯系原作者