AMD、CMD規范


本文原鏈接:https://cloud.tencent.com/developer/article/1177217

AMD && CMD

前言

JavaScript初衷:實現簡單的頁面交互邏輯,寥寥數語即可;

隨着web2.0時代的到來,Ajax技術得到廣泛應用,jQuery等前端庫層出不窮,前端代碼日益膨脹

問題:

這時候JavaScript作為嵌入式的腳本語言的定位動搖了,JavaScript卻沒有為組織代碼提供任何明顯幫助,甚至沒有類的概念,JavaScript極其簡單的代碼組織規范不足以駕馭如此龐大規模的代碼

一、模塊

模塊化:是一種處理復雜系統分解為代碼結構更合理,可維護性更高的可管理的模塊的方式。

在理想狀態下我們只需要完成自己部分的核心業務邏輯代碼,其他方面的依賴可以通過直接加載被人已經寫好模塊進行使用即可。

無模塊

<script src="jquery.js"></script>    <script src="jquery_scroller.js"></script>    <script src="main.js"></script>    <script src="other1.js"></script>    <script src="other2.js"></script>    <script src="other3.js"></script>

優點:

相比於使用一個js文件,這種多個js文件實現最簡單的模塊化的思想是進步的。 

缺點:

污染全局作用域。 因為每一個模塊都是暴露在全局的,簡單的使用,會導致全局變量命名沖突,當然,我們也可以使用命名空間的方式來解決。 對於大型項目,各種js很多,開發人員必須手動解決模塊和代碼庫的依賴關系,后期維護成本較高。 依賴關系不明顯,不利於維護。 比如main.js需要使用jquery,但是,從上面的文件中,我們是看不出來的,如果jquery忘記了,那么就會報錯。

二、CommonJS

CommonJs 是服務器端模塊的規范,Node.js采用了這個規范。

根據CommonJS規范,一個單獨的文件就是一個模塊。加載模塊使用require方法,該方法讀取一個文件並執行,最后返回文件內部的exports對象。

var math = require('math'); math.add(2, 3);

第二行math.add(2, 3),在第一行require('math')之后運行,因此必須等math.js加載完成。也就是說,如果加載時間很長,整個應用就會停在那里等。您會注意到 require 是同步的。

CommonJS 加載模塊是同步的,所以只有加載完成才能執行后面的操作。像Node.js主要用於服務器的編程,加載的模塊文件一般都已經存在本地硬盤,所以加載起來比較快,不用考慮異步加載的方式,所以CommonJS規范比較適用。但如果是瀏覽器環境,要從服務器加載模塊,這是就必須采用異步模式。所以就有了 AMD  CMD 解決方案。

三、AMD

AMD 即 Asynchronous Module Definition,中文名是異步模塊定義的意思。它是一個在瀏覽器端模塊化開發的規范

AMD也采用require()語句加載模塊,但是不同於CommonJS,它要求兩個參數:

require([module], callback);

第一個參數[module],是一個數組,里面的成員就是要加載的模塊;第二個參數callback,則是加載成功之后的回調函數。如果將前面的代碼改寫成AMD形式,就是下面這樣:

require(['math'], function (math) {   math.add(2, 3); });

math.add()與math模塊加載不是同步的,瀏覽器不會發生假死。所以很顯然,AMD比較適合瀏覽器環境。目前,主要有兩個Javascript庫實現了AMD規范:require.jscurl.js

與 RequireJS

AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出

AMD異步加載模塊。它的模塊支持對象 函數 構造器 字符串 JSON等各種類型的模塊。

//通過數組引入依賴 ,回調函數通過形參傳入依賴
define(['Module1', ‘Module2’], function (Module1, Module2) { function foo () { /// someing Module1.test(); } return {foo: foo} });
  • 第一個參數 id 為字符串類型,表示了模塊標識,為可選參數。若不存在則模塊標識應該默認定義為在加載器中被請求腳本的標識。如果存在,那么模塊標識必須為頂層的或者一個絕對的標識。
  • 第二個參數,dependencies ,是一個當前模塊依賴的,已被模塊定義的模塊標識的數組字面量。
  • 第三個參數,factory,是一個需要進行實例化的函數或者一個對象。

    創建模塊標識為 Module1 的模塊,依賴於 require, export,和標識為 beta 的模塊  

AMD規范允許輸出模塊兼容CommonJS規范,這時define方法如下:

define(function (require, exports, module) { var reqModule = require("./someModule"); requModule.test(); exports.asplode = function () { //someing } });

優點:

適合在瀏覽器環境中異步加載模塊。可以並行加載多個模塊。 缺點:

提高了開發成本,並且不能按需加載,而是必須提前加載所有的依賴。

四、CMD

CMD是SeaJS 在推廣過程中對模塊定義的規范化產出

CMD和AMD的區別有以下幾點:

1.對於依賴的模塊AMD是提前執行,CMD是延遲執行。不過RequireJS從2.0開始,也改成可以延遲執行(根據寫法不同,處理方式不通過)。

2.AMD推崇依賴前置(在定義模塊的時候就要聲明其依賴的模塊),CMD推崇依賴就近(只有在用到某個模塊的時候再去require——按需加載)。

//AMD
define(['./a','./b'], function (a, b) { //依賴一開始就寫好 a.test(); b.test(); }); //CMD define(function (requie, exports, module) { //依賴可以就近書寫 var a = require('./a'); a.test(); ... //軟依賴 if (status) { var b = requie('./b'); b.test(); } });

3.AMD的api默認是一個當多個用,CMD嚴格的區分推崇職責單一。例如:AMD里require分全局的和局部的。CMD里面沒有全局的 require,提供 seajs.use()來實現模塊系統的加載啟動。CMD里每個API都簡單純粹。

AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出,CMD是SeaJS 在推廣過程中被廣泛認知。RequireJs出自dojo加載器的作者James Burke,SeaJs出自國內前端大師玉伯。二者的區別,玉伯在12年如是說

RequireJS 和 SeaJS 都是很不錯的模塊加載器,兩者區別如下:

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

優點: 同樣實現了瀏覽器端的模塊化加載。 可以按需加載,依賴就近。

缺點: 依賴SPM打包,模塊的加載邏輯偏重。

 五、 ES6 模塊

CommonJS

  • 對於基本數據類型,屬於復制。即會被模塊緩存。同時,在另一個模塊可以對該模塊輸出的變量重新賦值。
  • 對於復雜數據類型,屬於淺拷貝。由於兩個模塊引用的對象指向同一個內存空間,因此對該模塊的值做修改時會影響另一個模塊。
  • 當使用require命令加載某個模塊時,就會運行整個模塊的代碼。
  • 當使用require命令加載同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊無論加載多少次,都只會在第一次加載時運行一次,以后再加載,就返回第一次運行的結果,除非手動清除系統緩存。
  • 循環加載時,屬於加載時執行。即腳本代碼在require的時候,就會全部執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出。

ES6模塊

  • ES6模塊中的值屬於【動態只讀引用】。
  • 對於只讀來說,即不允許修改引入變量的值,import的變量是只讀的,不論是基本數據類型還是復雜數據類型。當模塊遇到import命令時,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊里面去取值。
  • 對於動態來說,原始值發生變化,import加載的值也會發生變化。不論是基本數據類型還是復雜數據類型。
  • 循環加載時,ES6模塊是動態引用。只要兩個模塊之間存在某個引用,代碼就能夠執行。


免責聲明!

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



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