從 CommonJS 到 Sea.js

2019-08-14 14:35 更新

CommonJS 是什么

CommonJS 是一個(gè)有志于構(gòu)建 JavaScript 生態(tài)圈的組織。它有一個(gè) 郵件列表,很多開發(fā)者參與其中。 整個(gè)社區(qū)致力于提高 JavaScript 程序的可移植性和可交換性,無論是在服務(wù)端還是瀏覽器端。

CommonJS 模塊是什么

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

CommonJS 與瀏覽器

仔細(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ī)范

CommonJS 與 Sea.js

從上面可以看出,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)行。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)