為什么要有約定和構(gòu)建工具

2018-11-06 18:31 更新

在書寫 CMD 模塊時,需要遵守 require 書寫約定 。
在壓縮 CMD 模塊時,推薦使用配套的 構(gòu)建工具 來壓縮。

為什么要這么做呢?

CMD 模塊的構(gòu)建過程

CMD 模塊在構(gòu)建時,有兩個基本操作:

  1. 提取操作,用來提取模塊的標(biāo)識 id 和依賴 dependencies。假設(shè)模塊代碼為:

    a.js

    define(function(require, exports) {  var b = require('./b');
    })

    經(jīng)過提取操作后,a.js 的源碼會轉(zhuǎn)換成臨時文件:

    define('xxx/1.0.0/a', ['./b'], function(require, exports) {  var b = require('./b');
    })
  2. 壓縮操作。經(jīng)過上面的提取操作后,構(gòu)建工具就可以調(diào)用任何 JS 壓縮工具來進(jìn)行壓縮了,require 參數(shù)也可以被壓縮成任意字符。

可以看出,和普通壓縮工具相比,CMD 模塊的構(gòu)建過程中增加了 iddependencies 的提取操作。下面說明為什么需要預(yù)先提取這兩個信息。

為什么要提取 id

默認(rèn)情況下,書寫 CMD 模塊時,不需要手寫 id

a.js

define(function(require, exports) {
  ...
});

b.js

define(function(require, exports) {
  ...
});

上面兩個模塊,如果直接合并,會變成:

a+b.js

define(function(require, exports) {
  ...
});
define(function(require, exports) {
  ...
});

這會導(dǎo)致無法區(qū)分 define 對應(yīng)哪個模塊。因此在合并前,我們需要通過工具將 id 提取出來。

a+b.js:

define('a', function(require, exports) {
  ...
});
define('b', function(require, exports) {
  ...
});

此外,即便不合并,保持一個文件一個模塊,如果壓縮時不提取 id,那么在 IE6-9 下也有可能會出現(xiàn)問題。這是實(shí)現(xiàn)上的困難,具體請看源碼。如果要確保上線后在 IE 下沒問題,請務(wù)必要手寫或通過工具提取 id

為什么 require 要有書寫約定

在開發(fā)時,Sea.js 是如何知道一個模塊的具體依賴呢?

a.js

define(function(require, exports) {  var b = require('./b');  var c = require('./c');
});

Sea.js 在運(yùn)行 define 時,接受 factory 參數(shù),可以通過 factory.toString() 拿到源碼,再通過正則匹配 require 的方式來得到依賴信息。依賴信息是一個數(shù)組,比如上面 a.js 的依賴數(shù)組是:['./b', './c']

由于 Sea.js 的這個實(shí)現(xiàn)原理,使得書寫 CMD 模塊代碼時,必須遵守 require 書寫約定,否則獲取不到依賴數(shù)組,Sea.js 也就無法正確運(yùn)行。

而且正則匹配取依賴的實(shí)現(xiàn)方案并非百分百可靠,除了 require 關(guān)鍵字被壓縮的問題以外,對于一些極端情況無法保證正確性,特別對于壓縮后的代碼。有興趣的可以看看社區(qū)里關(guān)于 require 正則提取依賴的有獎挑戰(zhàn)。

為什么要提取依賴數(shù)組 dependencies

為了保證壓縮工具可以隨意壓縮代碼,構(gòu)建工具在提取 id 字符串時,同時也會提取 dependencies 數(shù)組。提取過后的代碼變成:

define('xxx/1.0.0/a', ['./b', './c'], function(require, exports) {  var b = require('./b');  var c = require('./c');
});

這樣,Sea.js 就不需要通過 factory.toString() 和正則匹配的方式來獲取依賴,直接從第二個參數(shù)中就可以拿到依賴數(shù)組。

這意味著,提取過 iddependencies 的模塊代碼,就可以用任何壓縮工具壓縮了。

注意,一旦設(shè)置了 define 的第二個參數(shù) dependencies,Sea.js 將不會用正則匹配的方式來獲取依賴,而直接將 dependencies 作為所有的依賴。

用普通壓縮工具如何壓縮 CMD 模塊

由于各種原因,暫時無法使用 Sea.js 配套的構(gòu)建工具來壓縮時,需要注意以下幾點(diǎn):

  1. 如果項(xiàng)目需要支持 IE,請手寫 id,即定義模塊時,需要人肉寫上第一個參數(shù),比如:

    define('a', function(require, exports) {
      ...
    });

    如果項(xiàng)目對性能有要求,上線后需要合并文件,也請確保手工寫上 id 參數(shù)。

  2. 壓縮時,不要壓縮 require 參數(shù),目前 UglifyJS 支持通過參數(shù)來指定保留名字:

    $ uglifyjs --reserved 'require' -o test-min.js test.js

或者自己寫工具來保證 iddependencies 的預(yù)先提取。

小結(jié)

如果使用 Sea.js,強(qiáng)烈推薦采用配套的構(gòu)建工具來壓縮、合并代碼。如果不這么做,可能會帶來不少額外的工作甚至隱患。

這是一把雙刃劍,目前業(yè)界還沒有『完美』的處理方案,都會在某些地方存在取舍和權(quán)衡。
如果這方面你有好的想法,歡迎與我們交流。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號