CommonJS 是一個(gè)有志于構(gòu)建 JavaScript 生態(tài)圈的組織。它有一個(gè) 郵件列表,很多開發(fā)者參與其中。 整個(gè)社區(qū)致力于提高 JavaScript 程序的可移植性和可交換性,無論是在服務(wù)端還是瀏覽器端。
JavaScript 并沒有內(nèi)置模塊系統(tǒng)(反正現(xiàn)在沒有,需要等到 ES6 的普遍支持,不知還需要多少年),于是 CommonJS 創(chuàng)造了自己的。 傳統(tǒng)的 CommonJS 模塊如下:
math.js
exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) {
sum += args[i++];
} return sum;
};
increment.js
var add = require('math').add;exports.increment = function(val) { return add(val, 1);
};
program.js
var inc = require('increment').increment;var a = 1;
inc(a); // 2
仔細(xì)看上面的代碼,您會(huì)注意到 require
是同步的。模塊系統(tǒng)需要同步讀取模塊文件內(nèi)容,并編譯執(zhí)行以得到模塊接口。
然而, 這在瀏覽器端問題多多。
瀏覽器端,加載 JavaScript 最佳、最容易的方式是在 document
中插入<script>
標(biāo)簽。但腳本標(biāo)簽天生異步,傳統(tǒng) CommonJS 模塊在瀏覽器環(huán)境中無法正常加載。
解決思路之一是,開發(fā)一個(gè)服務(wù)器端組件,對(duì)模塊代碼作靜態(tài)分析,將模塊與它的依賴列表一起返回給瀏覽器端。 這很好使,但需要服務(wù)器安裝額外的組件,并因此要調(diào)整一系列底層架構(gòu)。
另一種解決思路是,用一套標(biāo)準(zhǔn)模板來封裝模塊定義:
define(function(require, exports, module) { // The module code goes here});
這套模板代碼為模塊加載器提供了機(jī)會(huì),使其能在模塊代碼執(zhí)行之前,對(duì)模塊代碼進(jìn)行靜態(tài)分析,并動(dòng)態(tài)生成依賴列表。
為了讓靜態(tài)分析可行,需要遵守一些簡單的 規(guī)則。
把上面例子中的模塊封裝起來,可得到:
math.js
define(function(require, exports, module) { exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) {
sum += args[i++];
} return sum;
};
});
increment.js
define(function(require, exports, module) { var add = require('math').add; exports.increment = function(val) { return add(val, 1);
};
});
program.js
define(function(require, exports, module) { var inc = require('increment').increment; var a = 1;
inc(a); // 2});
上面是一種封裝方案,還有各種各樣的封裝方案,比如 AMD、Modules/Wrappings、CommonJS/Modules 2.0 等等模塊定義規(guī)范。
Sea.js 的封裝方案就是 CMD 規(guī)范:CMD 模塊定義規(guī)范
從上面可以看出,Sea.js 的初衷是為了讓 CommonJS Modules/1.1 的模塊能運(yùn)行在瀏覽器端,但由于瀏覽器和服務(wù)器的實(shí)質(zhì)差異,實(shí)際上這個(gè)夢無法完全達(dá)成,也沒有必要去達(dá)成。
更好的一種方式是,Sea.js 專注于 Web 瀏覽器端,CommonJS 則專注于服務(wù)器端,但兩者有共通的部分。對(duì)于需要在兩端都可以跑的模塊,可以 有便捷的方案來快速遷移。
目前 Sea.js 的模塊,如果沒有用到瀏覽器環(huán)境下的特有屬性,可以很方便跑在 NodeJS 端。只要在入口文件處,引入 Sea.js 的 Node.js 版本即可:
// 讓 Node 環(huán)境可以加載執(zhí)行 CMD 模塊require('seajs');var a = require('./a');
這樣,a.js
就可以是一個(gè)用 define
包裹起來的 CMD 模塊了。
CommonJS 的模塊需要跑在瀏覽器端時(shí),通過簡單封裝就行:
a.js
define(function(require, exports, module) { // a.js 原來的代碼});
這樣 a.js
就可以在瀏覽器端通過 Sea.js 加載運(yùn)行。當(dāng)然前提是 a.js
沒有利用到服務(wù)器特有屬性和模塊,比如 __dirname
、process
等。
通過上面的方案,我們就實(shí)現(xiàn)了 CommonJS 與 Sea.js 兩個(gè)生態(tài)圈的融合,可以彼此互通,讓我們書寫的 JavaScript 模塊可移植,可在不同平臺(tái)上運(yùn)行。
更多建議: