目前主流的JavaScript模塊有CommonJS嫉拐、AMD哩都、CMD、以及ES的模塊系統(tǒng)婉徘。
一漠嵌、CommonJS
commonJS的出發(fā)點(diǎn):js沒有完善的模塊系統(tǒng),但是隨著NodeJS的出現(xiàn)盖呼,讓js可以在任意地方運(yùn)行儒鹿,因此具備了大型項(xiàng)目的開發(fā)能力,CommonJS也在此時(shí)應(yīng)運(yùn)而生几晤。
NodeJS是commonJS的主要實(shí)踐者约炎,有四個(gè)環(huán)境變量Module,exports蟹瘾,require圾浅,global為它提供支持,實(shí)際使用時(shí)憾朴,用module.exports導(dǎo)出模塊(定義當(dāng)前模塊對(duì)外輸出的接口)贱傀,require加載模塊。
commonJS使用同步的方式加載模塊伊脓,在本地時(shí)府寒,因?yàn)槟K文件儲(chǔ)存在磁盤中,讀取速度很快报腔,所以沒有問題株搔,但是在瀏覽器中,因?yàn)榫W(wǎng)絡(luò)的問題纯蛾,所以更合理的方法是采用異步的方法纤房。
-暴露方法>module.exports = value或exports.xxx = value
-引入模塊>const xxx = require(xxx)
commonJS規(guī)范
1.一個(gè)文件就是一個(gè)模塊,具有單獨(dú)的作用域翻诉。
2.普通方式定義的變量炮姨、函數(shù)捌刮、對(duì)象都屬于該作用域中。
3.通過require加載模塊舒岸。
4.通過exports和module.exports來暴露模塊中的內(nèi)容绅作。
注意:
1.exports是module.exports的子集
2.所有代碼運(yùn)行在指定的模塊中,不會(huì)污染全局作用域
3.模塊可以被多次加載蛾派,但是只會(huì)在第一次加載時(shí)執(zhí)行俄认,之后就會(huì)講運(yùn)行結(jié)果緩存進(jìn)行下一次使用。
4.模塊加載順序洪乍,按照模塊出現(xiàn)的順序進(jìn)行加載眯杏。
5._dirname代表當(dāng)前文件所在的文件夾路徑
6._filename代表當(dāng)前模塊文件所在的文件夾路徑+文件名
7.當(dāng)exports和module.exports同時(shí)存在,module.exports會(huì)覆蓋exports壳澳。
8.當(dāng)模塊內(nèi)全是exports時(shí)岂贩,就相當(dāng)于module.exports
二、ES6模塊化
es6模塊化語(yǔ)言旨在成為瀏覽器和服務(wù)器的通用模塊解決方案巷波,通過export導(dǎo)出模塊萎津,通過import引入模塊,es6還提供了默認(rèn)導(dǎo)出的exports default命令褥紫,為模塊增加指定輸出,對(duì)應(yīng)的import不需要大括號(hào)瞪慧。
es6模塊不是對(duì)象髓考,import命令會(huì)被js引擎靜態(tài)分析(安全的編譯,優(yōu)化性能弃酌,靜態(tài)的將代碼加載到引用了的文件氨菇?具體不太懂,了解清楚后會(huì)發(fā)文)妓湘,編譯時(shí)就會(huì)引入模塊的代碼查蓉,而不是在代碼運(yùn)行的時(shí)候去加載,所以無法實(shí)現(xiàn)按條件加載榜贴。也正是因?yàn)檫@個(gè)豌研,使靜態(tài)加載成為可能。
1.export 將模塊中的代碼對(duì)外暴露唬党,可以導(dǎo)出的是一個(gè)對(duì)象包含的多個(gè)屬性方法鹃共,export.default只能導(dǎo)出一個(gè)可以不具名的函數(shù),我沒可以通過import引用驶拱,同時(shí)我沒也可以用require引入霜浴,因?yàn)閣ebpack引起了server相關(guān)。
2.import 引入需要用到的模塊蓝纲,在編譯時(shí)就會(huì)引入阴孟,所以不存在按需引入晌纫。
import {fn} from './xxx/xxx'(export的導(dǎo)出方式的引用方式)
import fn from './xxx/xxx1'(export.default的到處方式的引用方式)
三、AMD
Asynchronous Module Definition永丝,異步加載模塊锹漱。它是一個(gè)在瀏覽器端模塊化開發(fā)的規(guī)范,不是原生js的規(guī)范类溢,使用AMD進(jìn)行模塊開發(fā)需要使用到RequireJS函數(shù)庫(kù)凌蔬。
AMD規(guī)范采用異步方式加載模塊,模塊的加載不影響后續(xù)代碼的執(zhí)行闯冷,所有依賴這個(gè)模塊的語(yǔ)句都會(huì)定義一個(gè)回調(diào)函數(shù)砂心,當(dāng)模塊加載完成后會(huì)執(zhí)行這個(gè)回調(diào)函數(shù)。
使用require.js實(shí)現(xiàn)AMD規(guī)范的模塊化:用require.config()指定引用路徑等蛇耀,用defined()定義模塊辩诞,用require()引入模塊。
//定義模塊
defined('moduleName',['a','b'],function(ma,mb){
return someExportValue
})
//引入模塊
require(['a','b'],function(ma,mb)){
//*code*
}
1.函數(shù)庫(kù)requireJS主要解決的問題
-文件可能存在依賴關(guān)系纺涤,被依賴的文件需要早于依賴它的文件加載到瀏覽器中译暂。
-js在加載的時(shí)候?yàn)g覽器會(huì)停止頁(yè)面渲染,加載文件越多撩炊,頁(yè)面的響應(yīng)時(shí)間就會(huì)越長(zhǎng)外永。
-異步加載前置
2.語(yǔ)法
define(id,dependencies,factory)
-id 可選參數(shù),用來定義模塊的標(biāo)識(shí)拧咳,如果沒有提供該參數(shù)伯顶,將使用腳本文件名(去掉拓展名)
-dependencies是一個(gè)當(dāng)前模塊用來的模塊名稱數(shù)組。
-factory骆膝,工廠方法祭衩,模塊初始化要執(zhí)行的函數(shù)或是對(duì)象,如果是函數(shù)阅签,他應(yīng)該制備執(zhí)行一次掐暮,如果是對(duì)象,此對(duì)象應(yīng)該為模塊的輸出值政钟。
四路克、CMD
CMD是另一種js模塊化方案,它與AMD很類似养交,不同點(diǎn)在于:AMD推崇依賴前置衷戈、提前執(zhí)行,CMD推崇依賴就近层坠、延遲執(zhí)行殖妇。此規(guī)范其實(shí)是在sea.js推廣過程中產(chǎn)生的。
因?yàn)镃MD推從一個(gè)文件一個(gè)模塊破花,所以經(jīng)常就用文件名作為模塊id谦趣;CMD推推崇依賴就近疲吸,所以一般不再define的參數(shù)中寫依賴,而是在factory中寫
define(id,deps,factory)
factory有三個(gè)參數(shù):function(require,exports,module){}
1.require參數(shù)是第一個(gè)參數(shù)前鹅,是一個(gè)方法摘悴,接收模塊標(biāo)識(shí)作為唯一參數(shù),用來獲取其它模塊提供的接口舰绘;
2.exports蹂喻,是一個(gè)對(duì)象,用來向外提供模塊接口捂寿;
3.module口四,是一個(gè)對(duì)象,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法秦陋。
//定義沒有依賴的模塊
define(function(require,exports,module){
exports.xxx = value
module.exports = value
})
//定義有依賴的模塊
define(function(require,exports,module){
//同步引入模塊
var module1 = require('./module1.js')
//異步引入模塊
require.async('./module2.js',function(m2){
/***/
})
exports.xxx = value
}
//引入模塊
define(function(require){
const m1 = require('./module1.js');
m1.show()
})
五UMD通用模塊規(guī)范
一種整合了CommonJS和AMD規(guī)范的方法蔓彩,希望能解決跨平臺(tái)模塊方案。
運(yùn)行原理
-UMD先判斷是否支持Node.js模塊(exports是否存在)驳概,存在則用Node.js模塊模式赤嚼。
-在判斷是否支持AMD(define是否存在)存在則使用AMD加載模塊。
(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.eventUtil = factory();
}
})(this, function () {
//module ...
});
六顺又、總結(jié)
commonjs是同步加載的更卒,主要是在nodejs也就是服務(wù)端應(yīng)用的模塊化機(jī)制,通過Module.export導(dǎo)出聲明稚照。通過require('')加載蹂空。每個(gè)文件都是一個(gè)模塊。他有自己的作用域锐锣,文件內(nèi)的變量腌闯,屬性函數(shù)等不能被外界訪問绳瘟。node會(huì)將模塊緩存雕憔,第二次加載會(huì)直接在緩存中獲取。
AMD是異步加載的糖声。主要應(yīng)用在瀏覽器環(huán)境下斤彼,requireJS是遵循AMD規(guī)范的模塊化工具,他是通過define()定義聲明蘸泻,通過require('',function(){})加載琉苇。
es6的模塊化加載時(shí)通過export default導(dǎo)出,用import帶入悦施,可通過{}對(duì)導(dǎo)出的內(nèi)容進(jìn)行解構(gòu)并扇。
es6的模塊的運(yùn)行機(jī)制與common不一樣,js引擎對(duì)腳本靜態(tài)分析的時(shí)候抡诞,遇到模塊加載指令后會(huì)生成一個(gè)只讀引用穷蛹,等到腳本真正執(zhí)行的時(shí)候才會(huì)通過引用去模塊中獲取值土陪,在引用到執(zhí)行的過程中,模塊中的值發(fā)生了變化肴熏,導(dǎo)入的這里也會(huì)跟著變鬼雀,es6
模塊是動(dòng)態(tài)引用,并不會(huì)緩存值蛙吏。模塊里總是綁定其所在的模塊源哩。
關(guān)于模塊化,我認(rèn)為是構(gòu)建大型項(xiàng)目所必須的鸦做,讓代碼結(jié)構(gòu)更加清晰励烦,讓模塊之間的引用關(guān)系,以及具體作用功能更加清晰馁龟,方便了團(tuán)隊(duì)聯(lián)合開發(fā)崩侠。