在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模塊化編程正式登上舞臺。
定義模塊 根據CommonJS規范,一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,也就是說,在該模塊內部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性
模塊輸出: 模塊只有一個出口,
module.exports
對象,我們需要把模塊希望輸出的內容放入該對象加載模塊: 加載模塊使用
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主要解決兩個問題:
- 多個JS文件可能有依賴關系,被依賴的文件需要早于依賴它的文件加載到瀏覽器。
- 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性能好,只有用戶需要的時候才執行。