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

2018-11-06 18:31 更新

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

為什么要這么做呢?

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

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

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

    a.js

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

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

    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ù)先提取這兩個(gè)信息。

為什么要提取 id

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

a.js

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

b.js

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

上面兩個(gè)模塊,如果直接合并,會(huì)變成:

a+b.js

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

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

a+b.js:

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

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

為什么 require 要有書寫約定

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

a.js

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

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

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

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

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

為了保證壓縮工具可以隨意壓縮代碼,構(gòu)建工具在提取 id 字符串時(shí),同時(shí)也會(huì)提取 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() 和正則匹配的方式來獲取依賴,直接從第二個(gè)參數(shù)中就可以拿到依賴數(shù)組。

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

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

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

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

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

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

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

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

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

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

小結(jié)

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

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


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)