原因
javascript早期作為一門輕量級語言,用于在 Web 上與用戶進(jìn)行少量的交互,并沒有依賴管理的概念。但隨著ajax技術(shù)的發(fā)展,js被要求實(shí)現(xiàn)的功能越來越多。全局變量污染,依賴混亂等問題亟待解決。其他語言已經(jīng)實(shí)現(xiàn)的模塊化被提上日程。
前驅(qū)
在正式的模塊化方案發(fā)布之前,有人想了其他方法解決變量污染的問題
- 雅虎的開源組件庫 YUI Library
采用了類似java命名空間的方式,但是因?yàn)檎{(diào)用和封裝都太過復(fù)雜并沒有推廣起來
YUI.util.module.doSomthing();
- JQuery
運(yùn)用了閉包的特性,在加載之初執(zhí)行函數(shù),把JQuery變量掛載到全局的window對象上
(function(root){
root.JQuery = root.$ = JQuery
})(window)
正式起航
1、CommonJS -- 最早的解決方案
1.1 背景
- mozilla 的工程師為解決用于服務(wù)端的js的自動化測試的模塊導(dǎo)入問題,制定了一套模塊化導(dǎo)入的規(guī)范,命名為ServerJS(Modules/0.1),這就是CommonJS的前身。
- 后來正式更名為CommonJS(Modules/1.0),從命名可以看出其野心,企圖一統(tǒng)所有編程語言的模塊化方案。
- Node的創(chuàng)始人決定為其包依賴管理尋找解決辦法時選擇了CommonJS的規(guī)范來,我覺得對其的推廣起到了極大的促進(jìn)作用。
1.2 具體實(shí)現(xiàn)
- 在CommonJS中一個文件就是一個模塊,擁有自己獨(dú)立的作用域,變量及方法;
- 定義全局變量
require,通過在require中傳入唯一的標(biāo)識來引入文件的依賴模塊,其執(zhí)行結(jié)果就是模塊暴露的API; - 如果執(zhí)行失敗,
require會拋出一個錯誤; - 模塊通過
exports向外暴露API,導(dǎo)出的必須是一個object,暴露的API是該對象的屬性
//moudleA.js
moudle.exports = {
a: 1
};
//moudleB.js
var ma = require('./moudleA');
var b = ma.a + 2;
module.exports ={
b: b
};
分裂統(tǒng)一(AMD、CMD、UMD)
由于CommonJS是起源于服務(wù)端的,所以雖然說他是一門很好的模塊化解決辦法的實(shí)現(xiàn)方法,但是其并不能運(yùn)用于瀏覽器端。所以瀏覽器端需要新的實(shí)現(xiàn)方法。但是在實(shí)現(xiàn)方法上CommonJS內(nèi)部出現(xiàn)了分歧,從而誕生了AMD、CMD、UMD三種解決方法(當(dāng)然還有其他小眾的方法,因?yàn)槠涫褂萌藬?shù)較少這里不做討論)。
保守派 —— browserify
這一派的理念是不動CommonJS,為配合瀏覽器實(shí)現(xiàn),可以使用一些其他方法將其轉(zhuǎn)化成瀏覽器可以理解的方法。跟我們現(xiàn)在的Babel的實(shí)現(xiàn)思想是一樣的。browserify就是這一觀點(diǎn)下的產(chǎn)物。
激進(jìn)派—— AMD(RequireJS)
這一派的觀點(diǎn)是既然不適用了那就改,應(yīng)該依據(jù)瀏覽器的特點(diǎn)放棄require,改用回調(diào)方式。但是其觀點(diǎn)并沒有被CommonJS社區(qū)接受所以這一派最終脫離了CommonJS社區(qū)自立門戶,起初是專注于RequireJS的開發(fā),后來才起草了AMD 異步模塊定義規(guī)范(Async Module Definition)。
AMD 作為一個規(guī)范,只需定義其語法 API,而不關(guān)心其實(shí)現(xiàn)。AMD 規(guī)范簡單到只有一個 API,即 define 函數(shù):
define(id?, dependencies?, factory);
- 定義全局函數(shù) define(id, dependencies, factory),用于定義模塊。dependencies 為依賴的模塊數(shù)組,在 factory 中需傳入形參與之一一對應(yīng)。
- 如果 dependencies 的值中有 require、exports 或module,則與 CommonJS 中的實(shí)現(xiàn)保持一致。
- 如果 dependencies 省略不寫,則默認(rèn)為 ['require', 'exports', 'module'],factory 中也會默認(rèn)傳入三者。
- 如果 factory 為函數(shù),模塊可以通過以下三種方式對外暴漏 API:return 任意類型;exports.XModule = XModule、module.exports = XModule。
- 如果 factory 為對象,則該對象即為模塊的導(dǎo)出值。
中間派——CMD、UMD
1. CMD
這一派認(rèn)為require不用放棄,但export也可以導(dǎo)出除object以外的其他類型。淘寶玉伯寫出了SeaJS,提出了CMD(Common Module Definition)規(guī)范,其在國內(nèi)很火,但并未在國外流行。
CMD 規(guī)范的主要內(nèi)容與 AMD 大致相同,不過保留了 CommonJS 中最重要的延遲加載、就近聲明(就近依賴)特性。
// moudleA.js
define(function(require, exports, module) {
module.exports = {
a: 1
};
});
// moudleB.js
define(function(require, exports, module) {
var ma = require('./moudleB');
var b = ma.a + 2;
module.exports = {
b: b
};
});
2. UMD
UMD 即 Universal Module Definition 的縮寫,它本質(zhì)上并不是一個真正的模塊化方案,而是將 CommonJS 和 AMD 相結(jié)合。
((root, factory) => {
if (typeof define === 'function' && define.amd) {
//AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
//CommonJS
var $ = requie('jquery');
module.exports = factory($);
} else {
root.testModule = factory(root.jQuery);
}
})(this, ($) => {
//todo
});
正統(tǒng) —— ES6/ES2015
時間前進(jìn)到 2016 年 5 月,經(jīng)過了兩年的討論,ECMAScript 6.0 終于正式通過決議,成為了國際標(biāo)準(zhǔn)。
在這一標(biāo)準(zhǔn)中,首次引入了 import 和export 兩個 JavaScript 關(guān)鍵字,并提供了被稱為 ES Module 的模塊化方案。