模塊化編程:AMD&CMD&CommonJS

在JavaScript發展初期就是為了實現簡單的頁面交互邏輯,寥寥數語即可;如今CPU、瀏覽器性能得到了極大的提升,很多頁面邏輯遷移到了客戶端(表單驗證等),隨著web2.0時代的到來,Ajax技術得到廣泛應用,jQuery等前端庫層出不窮,前端代碼日益膨脹

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

原始寫法

function m1(){
  //...
}

function m2(){
  //...
}

這種做法的缺點很明顯:"污染"了全局變量,無法保證不與其他模塊發生變量名沖突,而且模塊成員之間看不出直接關系。

對象寫法

  var module1 = new Object({

    _count : 0,

    m1 : function (){
      //...
    },

    m2 : function (){
      //...
    }

  });

上面的函數m1()和m2(),都封裝在module1對象里。使用的時候,就是調用這個對象的屬性。
但是,這樣的寫法會暴露所有模塊成員,內部狀態可以被外部改寫。比如,外部代碼可以直接改變內部計數器的值。

立即執行函數寫法

  var module1 = (function(){

    var _count = 0;

    var m1 = function(){
      //...
    };

    var m2 = function(){
      //...
    };

    return {
      m1 : m1,
      m2 : m2
    };
  })();

使用上面的寫法,外部代碼無法讀取內部的_count變量。
上述做法就是我們模塊化的基礎,目前,通行的JavaScript模塊規范主要有兩種:CommonJS和AMD

CommonJS

因為在網頁端沒有模塊化編程只是頁面JavaScript邏輯復雜,但也可以工作下去,在服務器端卻一定要有模塊,所以雖然JavaScript在web端發展這么多年,第一個流行的模塊化規范卻由服務器端的JavaScript應用帶來,CommonJS規范是由NodeJS發揚光大,這標志著JavaScript模塊化編程正式登上舞臺。

  1. 定義模塊 根據CommonJS規范,一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,也就是說,在該模塊內部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性

  2. 模塊輸出: 模塊只有一個出口,module.exports對象,我們需要把模塊希望輸出的內容放入該對象

  3. 加載模塊: 加載模塊使用require方法,該方法讀取一個文件并執行,返回文件內部的module.exports對象。
    假定有一個數學模塊math.js,就可以像下面這樣加載,并調用其方法:

  var math = require('math');

  math.add(2,3); // 5

這種寫法適合服務端,因為在服務器讀取模塊都是在本地磁盤,加載速度很快,但是如果在客戶端,加載模塊的時候有可能出現“假死”狀況。比如上面的例子中math的調用必須等待math.js請求成功,加載完畢。那么,能不能異步加載模塊呢?這時就出現了AMD和CMD規范。

AMD

AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。它采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。
AMD 是 RequireJS 在推廣過程中對模塊定義的規范化的產出。
RequireJS主要解決兩個問題:

  1. 多個JS文件可能有依賴關系,被依賴的文件需要早于依賴它的文件加載到瀏覽器。
  2. js加載的時候瀏覽器會停止頁面渲染,加載文件過多,未響應時間就會過長。
    AMD也采用require()語句加載模塊,但是不同于CommonJS,它要求兩個參數,第一個參數[module],是一個數組,里面的成員就是要加載的模塊;第二個參數callback,則是加載成功之后的回調函數。如果將前面的代碼改寫成AMD形式,就是下面這樣:
  require(['math'], function (math) {

    math.add(2, 3);

  });

CMD

CMD 是 SeaJS 在推廣過程中對模塊定義的規范化產出。CMD 即Common Module Definition通用模塊定義,CMD規范是國內發展出來的,就像AMD有個requireJS,CMD有個瀏覽器的實現SeaJS,SeaJS要解決的問題和requireJS一樣,只不過在模塊定義方式和模塊加載(可以說運行、解析)時機上有所不同。
Sea.js推崇一個文件一個文件,遵循統一的寫法,使用Sea.js進行模塊化開發可以帶來很多好處:

  • 模塊的版本管理。通過別名等配置,配合構建工具,可以比較輕松地實現模塊的版本管理。
  • 提高可維護性。模塊化可以讓每個文件的職責單一,非常有利于代碼的維護.
  • 前端性能優化。Sea.js 通過異步加載模塊,這對頁面性能非常有益。Sea.js 還提供了 combo、flush 等插件,配合服務端,可以很好地對頁面性能進行調優。
  • 跨環境共享模塊。CMD 模塊定義規范與 Node.js 的模塊規范非常相近。通過 Sea.js 的 Node.js 版本,可以很方便實現模塊的跨服務器和瀏覽器共享。
    在CMD上寫一個模塊:
define(function(require, exports, module) { 
    var math = require('math'); 
    math.add(2,3); 
});

總結:

1. 為什么要使用模塊化?

  • 解決命名沖突
  • 文件依賴管理
  • 提高代碼可讀性
  • 代碼解耦,提高復用性

2. AMD和CMD的區別:

對依賴的處理不同:

  • AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊
  • CMD推崇就近依賴,只有在用到某個模塊的時候再去require
    對依賴模塊的執行時機不同:
  • AMD中模塊加載完就執行該模塊,所有模塊都執行完后會進入require的回調函數,執行主邏輯,結果就是依賴模塊的執行順序和書寫順序不一定一致,看網絡速度,誰先下載完誰先執行,但主邏輯一定在所有依賴加載完成后才執行。
  • CMD是先把所有的依賴模塊全部加載完后進入主邏輯,遇到require語句的時候才執行對應的模塊,結果是模塊的執行順序和書寫順序是完全一致的。
  • AMD用戶體驗好,依賴模塊提前執行了所以沒有延遲,CMD性能好,只有用戶需要的時候才執行。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容