在書寫 CMD 模塊時(shí),需要遵守 require 書寫約定 。
在壓縮 CMD 模塊時(shí),推薦使用配套的 構(gòu)建工具 來壓縮。
為什么要這么做呢?
CMD 模塊在構(gòu)建時(shí),有兩個(gè)基本操作:
提取操作,用來提取模塊的標(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');
})
壓縮操作。經(jīng)過上面的提取操作后,構(gòu)建工具就可以調(diào)用任何 JS 壓縮工具來進(jìn)行壓縮了,require
參數(shù)也可以被壓縮成任意字符。
可以看出,和普通壓縮工具相比,CMD 模塊的構(gòu)建過程中增加了 id
和 dependencies
的提取操作。下面說明為什么需要預(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)。
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ù)組。
這意味著,提取過 id
和 dependencies
的模塊代碼,就可以用任何壓縮工具壓縮了。
注意,一旦設(shè)置了
define
的第二個(gè)參數(shù)dependencies
,Sea.js 將不會(huì)用正則匹配的方式來獲取依賴,而直接將dependencies
作為所有的依賴。
由于各種原因,暫時(shí)無法使用 Sea.js 配套的構(gòu)建工具來壓縮時(shí),需要注意以下幾點(diǎn):
如果項(xiàng)目需要支持 IE,請(qǐng)手寫 id
,即定義模塊時(shí),需要人肉寫上第一個(gè)參數(shù),比如:
define('a', function(require, exports) {
...
});
如果項(xiàng)目對(duì)性能有要求,上線后需要合并文件,也請(qǐng)確保手工寫上 id
參數(shù)。
壓縮時(shí),不要壓縮 require
參數(shù),目前 UglifyJS 支持通過參數(shù)來指定保留名字:
$ uglifyjs --reserved 'require' -o test-min.js test.js
或者自己寫工具來保證 id
和 dependencies
的預(yù)先提取。
如果使用 Sea.js,強(qiáng)烈推薦采用配套的構(gòu)建工具來壓縮、合并代碼。如果不這么做,可能會(huì)帶來不少額外的工作甚至隱患。
這是一把雙刃劍,目前業(yè)界還沒有『完美』的處理方案,都會(huì)在某些地方存在取舍和權(quán)衡。
如果這方面你有好的想法,歡迎與我們交流。
更多建議: