前言:前端模塊化出現(xiàn)的緣由和實(shí)現(xiàn)的一些弊端
出現(xiàn)的緣由
為什么會(huì)出現(xiàn)前端模塊化呢梭灿,要想實(shí)現(xiàn)功能的復(fù)用蟀架,就需要把代碼引入重復(fù)利用重挑,如果不使用模塊化讼呢,我們之前會(huì)使用的方法有哪些呢?
1.直接定義全局function的方式睡扬,那么會(huì)出現(xiàn)函數(shù)命名污染的情況盟蚣,并且各個(gè)部分之間的關(guān)系也看不出來。
2.在命名空間(name space)中定義方法卖怜,定義一個(gè)obj = {function1() {},function2(){}};這樣有個(gè)問題就是函數(shù)內(nèi)部的成員可以被隨意修改屎开,很不安全。
3.通過IIFE模式马靠,也就是函數(shù)自調(diào)用閉包的方式奄抽,暴露給window,但是如果存在模塊之間的依賴甩鳄,就會(huì)很麻煩逞度。
4.IIFE增強(qiáng)模式:引入依賴。為了解決上面的問題娩贷,就出現(xiàn)了IIFE增強(qiáng)模式第晰,說白了就是傳參把其他的模塊給傳進(jìn)去锁孟,就出現(xiàn)了先后順序很重要的問題彬祖,如果前面的都還沒掛載到window上面,此時(shí)運(yùn)行后面的js代碼肯定就是undefined品抽。
因此储笑,為了解決1.命名污染。2.提升代碼的維護(hù)性圆恤。3.更好的分離代碼突倍,實(shí)現(xiàn)按需加載。4提高代碼的復(fù)用性等,我們需要采用模塊化規(guī)范羽历,這里就介紹比較常用的四種模塊化的規(guī)范焊虏,分別是commonJS,AMD,CMD和ES6四種秕磷。
一诵闭、commonJS
1.commonJS-node
node.js原生就支持commonJS的規(guī)范,所以在node.js中可以直接使用澎嚣,不需要引入其他的包了疏尿。commonJS是在服務(wù)端跑的,所以都是讀的本地磁盤中的內(nèi)容易桃,不存在異步的問題褥琐,所以require過來就直接可以用,因?yàn)槭峭降奈钪#詴?huì)有阻塞的情況敌呈,但是在瀏覽器端,一般這些js文件也就是模塊贩汉,很多是通過網(wǎng)絡(luò)請(qǐng)求過來的驱富,是異步的所以使用commonJS就不是那么合適。
語法就是exports和require兩個(gè)匹舞。
1.1exports可以一起導(dǎo)出褐鸥,也可以分開導(dǎo)出,require直接導(dǎo)入
上面是module1內(nèi)部的代碼赐稽,可以module.exports = {}的方式叫榕,exports就是一個(gè)對(duì)象
上面是module2的內(nèi)部代碼,exports對(duì)象也可以是一個(gè)方法
上面是module3的內(nèi)部代碼姊舵,通過exports.foo和exports.bar的方式分開添加
上面是app.js的內(nèi)部代碼晰绎,可以看到直接通過require('路徑')的方式把模塊直接給引入過來了,非常方便
1.2 關(guān)于package.json
package.json是通過npm.init初始化的時(shí)候就產(chǎn)生的記錄本包的詳細(xì)信息的文件括丁,包含了name(這個(gè)name不能包含中文荞下,也不能有大寫,在老版本的npm的時(shí)候史飞,不具有自動(dòng)轉(zhuǎn)小寫的功能)尖昏,version以及author等內(nèi)容,并且通過npm install uniq -save(運(yùn)行時(shí)依賴构资,這里的save在npm5之后抽诉,不加save也默認(rèn)就是save)或者npm install uniq --save-dev(開發(fā)時(shí)依賴),在dependencies中記錄
1.3 執(zhí)行app.js
因?yàn)槭窃趎ode環(huán)境中執(zhí)行吐绵,當(dāng)前webStorm執(zhí)行也是用的node迹淌,不是在瀏覽器環(huán)境中河绽,無論通過node app.js還是右鍵執(zhí)行app.js都可以完成。
1.4 commonJS的不足
既然commonJS已經(jīng)這么方便了唉窃,為啥會(huì)出現(xiàn)AMD和CMD以及ES6的后續(xù)的規(guī)范呢耙饰,就是因?yàn)閏ommonJS是在node中實(shí)現(xiàn)的,也就是node環(huán)境中可以支持require和exports纹份,但是在瀏覽器環(huán)境中榔幸,是不支持的,所以要想在瀏覽器端使用commonJS的規(guī)范的話矮嫉,就必須使用browserify這種第三方包把代碼給轉(zhuǎn)換成瀏覽器端支持的代碼削咆,后面將寫commonJS在瀏覽器端的實(shí)現(xiàn)。
2.commonJS-Browserify
由于前文中提到的commonJS規(guī)范在瀏覽器端無法適用的情況蠢笋,我們需要引入一個(gè)第三方包也就是Browserify拨齐,把原本只能在node環(huán)境中跑的代碼給轉(zhuǎn)換成瀏覽器端可以使用的代碼。
browserify的安裝(必須全局和局部都安裝):
①昨寞、全局安裝
npm install browserify -g
②瞻惋、局部安裝
npm install broserify --save-dev
安裝完成之后,運(yùn)行browserify js/src/app.js -o js/dist/bundle.js
上述指令中的-o援岩,代表的是output歼狼,后面js/dist/bundle.js就是輸出的目標(biāo)位置,沒有dist目錄的話會(huì)自動(dòng)創(chuàng)建享怀,而且這個(gè)命令運(yùn)行之后羽峰,命令行不會(huì)有什么打印,不代表沒執(zhí)行成功添瓷。(這里其他module1梅屉,module2這些都沒有變,和前面1當(dāng)中的代碼是一樣的鳞贷,只是在瀏覽器中執(zhí)行了)
在html中通過script標(biāo)簽引入那個(gè)bundle.js就可以了坯汤,接下來在瀏覽器中執(zhí)行。
上圖就是在瀏覽器環(huán)境中執(zhí)行的結(jié)果搀愧,如果script的標(biāo)簽src引入的不是這個(gè)bundle惰聂,直接是app.js的話,require這種語法咱筛,瀏覽器根本就不認(rèn)識(shí)搓幌,就會(huì)出現(xiàn)下面的這個(gè)情況。
二眷蚓、AMD規(guī)范
AMD(Asynchronous Module Definition異步模塊定義)規(guī)范相比于CMD來說應(yīng)用得更加廣泛一些鼻种。AMD是專門用于瀏覽器端的反番,模塊的加載是異步的沙热。
AMD規(guī)范是通過Require.js實(shí)現(xiàn)的叉钥,所以需要下載Require.js文件。
基本語法:
①篙贸、定義暴露模塊:如果是定義沒有依賴的模塊
define(function(){
return 模塊
})
如果是定義有依賴的模塊
define(['module1','module2'],function(module1,module2) {
return 模塊
})
②投队、引入使用模塊
requirejs(['module1','module2'],function(m1,m2) {
使用m1/m2
})
2.1沒有AMD時(shí)候的模塊化實(shí)現(xiàn)方式
如上圖所示,在js文件夾中創(chuàng)建了alert和dataService兩個(gè)模塊爵川,這些模塊之間都是通過IIFE也就是立即執(zhí)行函數(shù)來把模塊內(nèi)的方法暴露給window對(duì)象的敷鸦,三者之間存在依賴關(guān)系,必須按照先后順序加入script標(biāo)簽寝贡,先dataService扒披,后alert再app.js這三個(gè)加載順序,這就是沒有AMD的時(shí)候的執(zhí)行方法圃泡,就是暴露給window碟案,但這樣的缺陷是非常明顯的,就是模塊之間的依賴必須要手動(dòng)去定好颇蜡,不然一定會(huì)存在undefined的情況价说。
2.2 采用AMD-RequireJS的方式實(shí)現(xiàn)模塊化
上圖是使用require.js來實(shí)現(xiàn)的方式,就是script標(biāo)簽的scr是require.js风秤,但是有一個(gè)主js的入口鳖目,就是data-main屬性,里面指定了主文件的入口缤弦。
上圖就是main.js的代碼领迈。里面是一個(gè)立即執(zhí)行函數(shù),因?yàn)樗恍枰傧蛲獗┞督涌诎澹苯泳褪褂胷equirejs方法惦费,把需要的模塊名字傳入,然后后面回調(diào)函數(shù)進(jìn)行使用就可以了抢韭。
上面是alerter薪贫,他引入了dataService模塊和jquery(必須小寫)模塊,回調(diào)函數(shù)中刻恭,就使用了其他的模塊瞧省。
這個(gè)dataService,是不需要依賴其他模塊的鳍贾,所以沒有傳前面的數(shù)組鞍匾,直接定義好,然后暴露接口就可以了骑科。
上面是層級(jí)目錄橡淑,記得libs下面有這個(gè)require.js
這個(gè)部分就是requirejs的配置,必須配置咆爽,否則根本不知道你數(shù)組里頭引入梁棠,或者定義的時(shí)候置森,那個(gè)模塊到底是誰,后面映射了路徑符糊。shim用來解決那些不支持AMD規(guī)范的js包的凫海,就比如angular,本來就是不支持的男娄,你配置了path也沒用行贪,配置shim就可以了,上面也可以配置baseUrl模闲。
三建瘫、CMD規(guī)范
CMD規(guī)范用的相對(duì)少一些,是阿里開發(fā)的尸折,但現(xiàn)在賣給外國人了暖混,使用起來的語法有點(diǎn)像CommonJS和AMD的結(jié)合版,SeaJS實(shí)現(xiàn)了CMD規(guī)范翁授,所以需要下載這個(gè)seajs拣播。
使用語法:
①、定義沒有依賴的模塊
define(function(require,exports,module) {
exports.xxx = value;
module.exports = value;
//這個(gè)和commonJS很像收擦,關(guān)鍵就是那個(gè)exports對(duì)象
})
②贮配、定義有依賴的模塊
define(function(require,exports,module) {
var module2 = require('./module2');
require.async('./module3',function(m3) {
///使用module3,是異步的塞赂,到時(shí)候module3拿到了就給回調(diào)直接執(zhí)行
})
exports.xxx = value
})
③泪勒、引入使用模塊
define(function(require) {
var m1 = require('./module1')
var m4 = require('./module4')
//執(zhí)行...
})
上圖是html里面的使用方法,先引入sea.js宴猾,然后seajs.use一下main.js圆存。
main.js里面因?yàn)橹皇窃谑褂茫圆恍枰雍竺娴膃xports和module仇哆,當(dāng)然你開心也可以加上沦辙。
里面的引入方式,還是一樣的讹剔,就是require就可以了油讯,很像commonjs。
上面是module1延欠,接下來以此就是四個(gè)module陌兑。
上面是module3和2,都是一個(gè)沒有依賴的由捎。
上面是module4兔综,依賴了2和3。前面mainjs引入了module1和4,執(zhí)行后發(fā)現(xiàn)異步調(diào)用的3被放在后面了软驰,說明確實(shí)異步了涧窒,結(jié)果就是1243。
上面是seajs的位置碌宴,不要忘記。
對(duì)比CMD和AMD可以發(fā)現(xiàn)蒙畴,AMD的依賴是前置的贰镣,在一個(gè)數(shù)組里頭,直接就先加載了再執(zhí)行后面的代碼膳凝,而CMD的依賴是后置的碑隆,只有需要的時(shí)候,才會(huì)去執(zhí)行模塊的加載蹬音。
四上煤、ES6
ES6需要安裝一些加載插件:babel-cli,babel-preset-es2015和browserify,babel-cli主要是實(shí)現(xiàn)命令行著淆,執(zhí)行babel指令可以運(yùn)行劫狠,后面那個(gè)babel-preset-es2015才是把ES6轉(zhuǎn)成ES5的工具,babel不僅可以完成es6轉(zhuǎn)es5永部,還能轉(zhuǎn)其他的独泞,轉(zhuǎn)啥就下載啥,所以這里就需要下載這個(gè)babel-preset-es2015苔埋。
1.npm install babel-cli browserify -g
全局安裝babel-cli這樣我們?cè)谌魏蔚胤蕉伎梢詧?zhí)行babel命令了懦砂,browserify我們前面已經(jīng)安裝過了,現(xiàn)在也安裝一下吧组橄,反正也不吃虧荞膘。
2.npm install babel-preset-es2015 --save-dev
這個(gè)很明顯是開發(fā)時(shí)候的依賴,轉(zhuǎn)了到時(shí)候運(yùn)行的時(shí)候就不需要再轉(zhuǎn)了嘛玉工。
3.定義.babelrc
不要忘記前面那個(gè). (點(diǎn))羽资,在根目錄里面新建一個(gè).babelrc,加了點(diǎn)的時(shí)候,webstorm自動(dòng)把它識(shí)別為json文件了遵班。babelrc也能在package.json里面配置
{
"name":'pname',
"babel": {
//config
}
}
我們這里直接在根目錄配置babelrc,rc代表的是run control運(yùn)行控制削罩。
4.編寫模塊之間的依賴
5.編譯
因?yàn)榍懊鎸懙膉s文件,首先瀏覽器根本就不認(rèn)識(shí)ES6的語法费奸,因此需要babel來轉(zhuǎn)成es5弥激,另外,需要把它弄到瀏覽器環(huán)境中能執(zhí)行愿阐,所以必須browserify
5.1微服、babel js/src -d js/lib
-d就是目標(biāo)是js/lib文件夾
-d前面就是需要編譯的整個(gè)文件夾路徑,里頭都需要編譯
5.2 browserify js/lib/main.js -o js/dist/bundle.js
這個(gè)就是把他弄到服務(wù)器端跑的工具
上面兩兩步編譯之后缨历,瀏覽器就能夠識(shí)別并運(yùn)行了以蕴。
6.默認(rèn)暴露的寫法(補(bǔ)充)
1.值得注意的一點(diǎn):package.json中name的值糙麦,不要取跟github上常用包一樣的名字,否則會(huì)死活無法下載下來丛肮。
2.安裝babel必須安裝babel-cli赡磅,cli就是command line interface也就是命令行接口,比如node里面的npm為啥可以執(zhí)行宝与,是因?yàn)槔锩婢陀衏li焚廊,存了各種命令,但是babel自身沒有习劫,所以需要手動(dòng)下載咆瘟。
3.npm install jquery@1指定下載jquery1里面的最新版本,默認(rèn)包就是import $ from 'jquery'不需要加路徑名诽里,而且也是默認(rèn)暴露的