web前端教程分享js中的模塊化一:我們知道最常見(jiàn)的模塊化方案有CommonJS、AMD、CMD、ES6,AMD規(guī)范一般用于瀏覽器,異步的,因?yàn)槟K加載是異步的,js解釋是同步的,所以有時(shí)候?qū)е乱蕾囘€沒(méi)加載完畢,同步的代碼運(yùn)行結(jié)束;CommonJS規(guī)范一般用于服務(wù)端,同步的,因?yàn)樵诜?wù)器端所有文件都存儲(chǔ)在本地的硬盤(pán)上,傳輸速率快而且穩(wěn)定。 1.script標(biāo)簽引入最開(kāi)始的時(shí)候,多個(gè)script標(biāo)簽引入js文件。但是,這種弊端也很明顯,很多個(gè)js文件合并起來(lái),也是相當(dāng)于一個(gè)script,造成變量污染。項(xiàng)目大了,不想變量污染也是很難或者不容易做到,開(kāi)發(fā)和維護(hù)成本高。 而且對(duì)于標(biāo)簽的順序,也是需要考慮一陣,還有加載的時(shí)候同步,更加是一種災(zāi)難,幸好后來(lái)有了渲染完執(zhí)行的defer和下載完執(zhí)行的async,進(jìn)入新的時(shí)代了。 接著,就有各種各樣的動(dòng)態(tài)創(chuàng)建script標(biāo)簽的方法,最終發(fā)展到了上面的幾種方案。 2.AMD與CMD2.1AMD異步模塊定義,提供定義模塊及異步加載該模塊依賴的機(jī)制。AMD遵循依賴前置,代碼在一旦運(yùn)行到需要依賴的地方,就馬上知道依賴是什么。而無(wú)需遍歷整個(gè)函數(shù)體找到它的依賴,因此性能有所提升。但是開(kāi)發(fā)者必須先前知道依賴具體有什么,并且顯式指明依賴,使得開(kāi)發(fā)工作量變大。而且,不能保證模塊加載的時(shí)候的順序。 典型代表requirejs。require.js在聲明依賴的模塊時(shí)會(huì)立刻加載并執(zhí)行模塊內(nèi)的代碼。require函數(shù)讓你能夠隨時(shí)去依賴一個(gè)模塊,即取得模塊的引用,從而即使模塊沒(méi)有作為參數(shù)定義,也能夠被使用。他的風(fēng)格是依賴注入,比如: /api.js define('myMoudle',['foo','bar'],function(foo,bar){ //引入了foo和bar,利用foo、bar來(lái)做一些事情 return { baz:function(){return 'api'} } }); require(['api'],function(api) { console.log(api.baz()) }) 復(fù)制代碼 然后你可以在中間隨時(shí)引用模塊,但是模塊第一次初始化的時(shí)間比較長(zhǎng)。這就像開(kāi)始的時(shí)候很拼搏很辛苦,到最后是美滋滋。 2.2CMD通用模塊定義,提供模塊定義及按需執(zhí)行模塊。遵循依賴就近,代碼在運(yùn)行時(shí),最開(kāi)始的時(shí)候是不知道依賴的,需要遍歷所有的require關(guān)鍵字,找出后面的依賴。一個(gè)常見(jiàn)的做法是將function toString后,用正則匹配出require關(guān)鍵字后面的依賴。CMD 里,每個(gè) API 都簡(jiǎn)單純粹??梢宰尀g覽器的模塊代碼像node一樣,因?yàn)橥剿砸氲捻樞蚴悄芸刂频摹?對(duì)于典型代表seajs,一般是這樣子: define(function(require,exports,module){ //...很多代碼略過(guò) var a = require('./a'); //要用到a,于是引入了a //做一些和模塊a有關(guān)的事情 }); 復(fù)制代碼 對(duì)于b.js依賴a.js //a.js define(function(require, exports) { exports.a = function(){//也可以把他暴露出去 // 很多代碼 }; }); //b.js define(function(require,exports){ //前面干了很多事情,突然想要引用a了 var fun = require('./a'); ????console.log(fun.a()); // 就可以調(diào)用到及執(zhí)行a函數(shù)了。 }) //或者可以u(píng)se seajs.use(['a.js'], function(a){ //做一些事情 }); 復(fù)制代碼 AMD和CMD對(duì)比: AMD 推崇依賴前置、提前執(zhí)行,CMD推崇依賴就近、延遲執(zhí)行。 AMD需要先列出清單,后面使用的時(shí)候隨便使用(依賴前置),異步,特別適合瀏覽器環(huán)境下使用(底層其實(shí)就是動(dòng)態(tài)創(chuàng)建script標(biāo)簽)。而且API 默認(rèn)是一個(gè)當(dāng)多個(gè)用。 CMD不需要知道依賴是什么,到了改需要的時(shí)候才引入,而且是同步的,就像臨時(shí)抱佛腳一樣。 對(duì)于客戶端的瀏覽器,一說(shuō)到下載、加載,肯定就是和異步脫不了關(guān)系了,注定瀏覽器一般用AMD更好了。但是,CMD的api都是有區(qū)分的,局部的require和全局的require不一樣。 3.CommonJS與ES63.1 ES6ES6模塊的script標(biāo)簽有點(diǎn)不同,需要加上type='module' <script src='./a.js' type='module'>...</script> 復(fù)制代碼 對(duì)于這種標(biāo)簽都是異步加載,而且是相當(dāng)于帶上defer屬性的script標(biāo)簽,不會(huì)阻塞頁(yè)面,渲染完執(zhí)行。但是你也可以手動(dòng)加上defer或者async,實(shí)現(xiàn)期望的效果。 ES6模塊的文件后綴是mjs,通過(guò)import引入和export導(dǎo)出。我們一般是這樣子: //a.mjs import b from 'b.js' //b.mjs export default b 復(fù)制代碼 ES6畢竟是ES6,模塊內(nèi)自帶嚴(yán)格模式,而且只在自身作用域內(nèi)運(yùn)行。在ES6模塊內(nèi)引入其他模塊就要用import引入,暴露也要用export暴露。另外,一個(gè)模塊只會(huì)被執(zhí)行一次。 import是ES6新語(yǔ)法,可靜態(tài)分析,提前編譯。他最終會(huì)被js引擎編譯,也就是可以實(shí)現(xiàn)編譯后就引入了模塊,所以ES6模塊加載是靜態(tài)化的,可以在編譯的時(shí)候確定模塊的依賴關(guān)系以及輸入輸出的變量。ES6可以做到編譯前分析,而CMD和AMD都只能在運(yùn)行時(shí)確定具體依賴是什么。 3.2CommonJS一般服務(wù)端的文件都在本地的硬盤(pán)上面。對(duì)于客戶,他們用的瀏覽器是要從這里下載文件的,在服務(wù)端一般讀取文件非???,所以同步是不會(huì)有太大的問(wèn)題。require的時(shí)候,馬上將require的文件代碼運(yùn)行 代表就是nodejs了。用得最多的,大概就是: //app.js var route = require('./route.js')//讀取控制路由的js文件 //route.js var route = {......} module.exports = route 復(fù)制代碼 require 第一次加載腳本就會(huì)馬上執(zhí)行腳本,生成一個(gè)對(duì)象 區(qū)別: CommonJS運(yùn)行時(shí)加載,輸出的是值的拷貝,是一個(gè)對(duì)象(都是由module.export暴露出去的),可以直接拿去用了,不用再回頭找。所以,當(dāng)module.export的源文件里面一些原始類型值發(fā)生變化,require這邊不會(huì)隨著這個(gè)變化而變化的,因?yàn)楸痪彺媪恕5怯幸环N常規(guī)的操作,寫(xiě)一個(gè)返回那個(gè)值的函數(shù)。就像angular里面$watch數(shù)組里面的每一個(gè)對(duì)象,舊值是直接寫(xiě)死,新值是寫(xiě)一個(gè)返回新值的函數(shù),這樣子就不會(huì)寫(xiě)死。module.export輸出一個(gè)取值的函數(shù),調(diào)用的時(shí)候就可以拿到變化的值。 ES6是編譯時(shí)輸出接口,輸出的是值的引用,對(duì)外的接口只是一種靜態(tài)的概念,在靜態(tài)解釋后已經(jīng)形成。當(dāng)腳本運(yùn)行時(shí),根據(jù)這個(gè)引用去原本的模塊內(nèi)取值。所以不存在緩存的情況,import的文件變了,誰(shuí)發(fā)出import的也是拿到這個(gè)變的值。模塊里面的變量綁定著他所在的模塊。另外,通過(guò)import引入的這個(gè)變量是只讀的,試圖進(jìn)行對(duì)他賦值將會(huì)報(bào)錯(cuò)。 |
|
來(lái)自: 好程序員IT > 《web前端培訓(xùn)教程》