js模块化历程
本人:(這篇文章作者寫的真是不錯(cuò),通讀一遍,js模塊化思想 基本屢順了,遂摘之,以備后用) 以下是摘文:
摘自:http://www.cnblogs.com/lvdabao/p/js-modules-develop.html?
這是一篇關(guān)于js模塊化歷程的長(zhǎng)長(zhǎng)的流水賬,記錄js模塊化思想的誕生與變遷,展望ES6模塊化標(biāo)準(zhǔn)的未來(lái)。經(jīng)歷過(guò)這段歷史的人或許會(huì)感到滄桑,沒(méi)經(jīng)歷過(guò)的人也應(yīng)該知道這段歷史。?
無(wú)模塊時(shí)代
在ajax還未提出之前,js還只是一種“玩具語(yǔ)言”,由Brendan Eich花了不到十天時(shí)間發(fā)明,用來(lái)在網(wǎng)頁(yè)上進(jìn)行表單校驗(yàn)、實(shí)現(xiàn)簡(jiǎn)單的動(dòng)畫效果等等,你可以回想一下那個(gè)網(wǎng)頁(yè)上到處有公告塊飄來(lái)飄去的時(shí)代。 這個(gè)時(shí)候并沒(méi)有前端工程師,服務(wù)端工程師只需在頁(yè)面上隨便寫寫js就能搞定需求。那個(gè)時(shí)候的前端代碼大概像這樣: if(xx){//....... } else{//xxxxxxxxxxx } for(var i=0; i<10; i++){//........ } element.onclick = function(){//....... } 代碼簡(jiǎn)單的堆在一起,只要能從上往下依次執(zhí)行就可以了。?
模塊萌芽時(shí)代
2006年,ajax的概念被提出,前端擁有了主動(dòng)向服務(wù)端發(fā)送請(qǐng)求并操作返回?cái)?shù)據(jù)的能力,隨著Google將此概念的發(fā)揚(yáng)光大,傳統(tǒng)的網(wǎng)頁(yè)慢慢的向“富客戶端”發(fā)展。前端的業(yè)務(wù)邏輯越來(lái)越多,代碼也越來(lái)越多,于是一些問(wèn)題就暴漏了出來(lái): 1. 全局變量的災(zāi)難 小明定義了 i=1 小剛在后續(xù)的代碼里:i=0 小明在接下來(lái)的代碼里:if(i==1){...} //悲劇 2. 函數(shù)命名沖突 項(xiàng)目中通常會(huì)把一些通用的函數(shù)封裝成一個(gè)文件,常見(jiàn)的名字有utils.js、common.js... 小明定義了一個(gè)函數(shù):function formatData(){ ? } 小剛想實(shí)現(xiàn)類似功能,于是這么寫:function formatData2(){ ? } 小光又有一個(gè)類似功能,于是:function formatData3(){ ? } ...... 避免命名沖突就只能這樣靠丑陋的方式人肉進(jìn)行。 3. 依賴關(guān)系不好管理 b.js依賴a.js,標(biāo)簽的書(shū)寫順序必須是 <script type="text/javascript" src="a.js"></script> <script type="text/javascript" src="b.js"></script> 順序不能錯(cuò),也不能漏寫某個(gè)。在多人開(kāi)發(fā)的時(shí)候很難協(xié)調(diào)。 萌芽時(shí)代的解決方案: 1. 用自執(zhí)行函數(shù)來(lái)包裝代碼 modA = function(){var a,b; //變量a、b外部不可見(jiàn)return {add : function(c){a + b + c;},format: function(){//......}} }() 這樣function內(nèi)部的變量就對(duì)全局隱藏了,達(dá)到是封裝的目的。但是這樣還是有缺陷的,modA這個(gè)變量還是暴漏到全局了,隨著模塊的增多,全局變量還是會(huì)越來(lái)越多。 2. java風(fēng)格的命名空間 為了避免全局變量造成的沖突,人們想到或許可以用多級(jí)命名空間來(lái)進(jìn)行管理,于是,代碼就變成了這個(gè)風(fēng)格: app.util.modA = xxx; app.tools.modA = xxx; app.tools.modA.format = xxx; Yahoo的YUI早期就是這么做的,調(diào)用的時(shí)候不得不這么寫: app.tools.modA.format(); 這樣調(diào)用函數(shù),寫寫都會(huì)覺(jué)得惡心,所以這種方式并沒(méi)有被很多人采用,YUI后來(lái)也不用這種方式了。 3. jQuery風(fēng)格的匿名自執(zhí)行函數(shù) (function(window){//代碼window.jQuery = window.$ = jQuery;//通過(guò)給window添加屬性而暴漏到全局 })(window); jQuery的封裝風(fēng)格曾經(jīng)被很多框架模仿,通過(guò)匿名函數(shù)包裝代碼,所依賴的外部變量傳給這個(gè)函數(shù),在函數(shù)內(nèi)部可以使用這些依賴,然后在函數(shù)的最后把模塊自身暴漏給window。 如果需要添加擴(kuò)展,則可以作為jQuery的插件,把它掛載到$上。 這種風(fēng)格雖然靈活了些,但并未解決根本問(wèn)題:所需依賴還是得外部提前提供、還是增加了全局變量。?
模塊化面臨什么問(wèn)題
從以上的嘗試中,可以歸納出js模塊化需要解決那些問(wèn)題: 1. 如何安全的包裝一個(gè)模塊的代碼?(不污染模塊外的任何代碼) 2. 如何唯一標(biāo)識(shí)一個(gè)模塊? 3. 如何優(yōu)雅的把模塊的API暴漏出去?(不能增加全局變量) 4. 如何方便的使用所依賴的模塊? 圍繞著這些問(wèn)題,js模塊化開(kāi)始了一段艱苦而曲折的征途。?
源自nodejs的規(guī)范CommonJs
2009年,nodejs橫空出世,開(kāi)創(chuàng)了一個(gè)新紀(jì)元,人們可以用js來(lái)編寫服務(wù)端的代碼了。如果說(shuō)瀏覽器端的js即便沒(méi)有模塊化也可以忍的話,那服務(wù)端是萬(wàn)萬(wàn)不能的。 大牛云集的CommonJs社區(qū)發(fā)力,制定了Modules/1.0(http://wiki.commonjs.org/wiki/Modules/1.0)規(guī)范,首次定義了一個(gè)模塊應(yīng)該長(zhǎng)啥樣。具體來(lái)說(shuō),Modules/1.0規(guī)范包含以下內(nèi)容: 1. 模塊的標(biāo)識(shí)應(yīng)遵循的規(guī)則(書(shū)寫規(guī)范) 2. 定義全局函數(shù)require,通過(guò)傳入模塊標(biāo)識(shí)來(lái)引入其他模塊,執(zhí)行的結(jié)果即為別的模塊暴漏出來(lái)的API 3. 如果被require函數(shù)引入的模塊中也包含依賴,那么依次加載這些依賴 4. 如果引入模塊失敗,那么require函數(shù)應(yīng)該報(bào)一個(gè)異常 5. 模塊通過(guò)變量exports來(lái)向往暴漏API,exports只能是一個(gè)對(duì)象,暴漏的API須作為此對(duì)象的屬性。 此規(guī)范一出,立刻產(chǎn)生了良好的效果,由于其簡(jiǎn)單而直接,在nodejs中,這種模塊化方案立刻被推廣開(kāi)了。 遵循commonjs規(guī)范的代碼看起來(lái)是這樣的:(來(lái)自官方的例子) //math.js exports.add = function() {var sum = 0, i = 0, args = arguments, l = args.length;while (i < l) {sum += args[i++];}return sum; }; //increment.js var add = require('math').add; exports.increment = function(val) {return add(val, 1); }; //program.js var inc = require('increment').increment; var a = 1; inc(a); // 2?
服務(wù)端向前端進(jìn)軍
Modules/1.0規(guī)范源于服務(wù)端,無(wú)法直接用于瀏覽器端,原因表現(xiàn)為: 1. 外層沒(méi)有function包裹,變量全暴漏在全局。如上面例子中increment.js中的add。 2. 資源的加載方式與服務(wù)端完全不同。服務(wù)端require一個(gè)模塊,直接就從硬盤或者內(nèi)存中讀取了,消耗的時(shí)間可以忽略。而瀏覽器則不同,需要從服務(wù)端來(lái)下載這個(gè)文件,然后運(yùn)行里面的代碼才能得到API,需要花費(fèi)一個(gè)http請(qǐng)求,也就是說(shuō),require后面的一行代碼,需要資源請(qǐng)求完成才能執(zhí)行。由于瀏覽器端是以插入<script>標(biāo)簽的形式來(lái)加載資源的(ajax方式不行,有跨域問(wèn)題),沒(méi)辦法讓代碼同步執(zhí)行,所以像commonjs那樣的寫法會(huì)直接報(bào)錯(cuò)。 所以,社區(qū)意識(shí)到,要想在瀏覽器環(huán)境中也能模塊化,需要對(duì)規(guī)范進(jìn)行升級(jí)。順便說(shuō)一句,CommonJs原來(lái)是叫ServerJs,從名字可以看出是專攻服務(wù)端的,為了統(tǒng)一前后端而改名CommonJs。(論起名的重要性~) 而就在社區(qū)討論制定下一版規(guī)范的時(shí)候,內(nèi)部發(fā)生了比較大的分歧,分裂出了三個(gè)主張,漸漸的形成三個(gè)不同的派別: 1.Modules/1.x派 這一波人認(rèn)為,在現(xiàn)有基礎(chǔ)上進(jìn)行改進(jìn)即可滿足瀏覽器端的需要,既然瀏覽器端需要function包裝,需要異步加載,那么新增一個(gè)方案,能把現(xiàn)有模塊轉(zhuǎn)化為適合瀏覽器端的就行了,有點(diǎn)像“保皇派”。基于這個(gè)主張,制定了Modules/Transport(http://wiki.commonjs.org/wiki/Modules/Transport)規(guī)范,提出了先通過(guò)工具把現(xiàn)有模塊轉(zhuǎn)化為復(fù)合瀏覽器上使用的模塊,然后再使用的方案。 browserify就是這樣一個(gè)工具,可以把nodejs的模塊編譯成瀏覽器可用的模塊。(Modules/Transport規(guī)范晦澀難懂,我也不確定browserify跟它是何關(guān)聯(lián),有知道的朋友可以講一下) 目前的最新版是Modules/1.1.1(http://wiki.commonjs.org/wiki/Modules/1.1.1),增加了一些require的屬性,以及模塊內(nèi)增加module變量來(lái)描述模塊信息,變動(dòng)不大。 2.?Modules/Async派 這一波人有點(diǎn)像“革新派”,他們認(rèn)為瀏覽器與服務(wù)器環(huán)境差別太大,不能沿用舊的模塊標(biāo)準(zhǔn)。既然瀏覽器必須異步加載代碼,那么模塊在定義的時(shí)候就必須指明所依賴的模塊,然后把本模塊的代碼寫在回調(diào)函數(shù)里。模塊的加載也是通過(guò)下載-回調(diào)這樣的過(guò)程來(lái)進(jìn)行,這個(gè)思想就是AMD的基礎(chǔ),由于“革新派”與“保皇派”的思想無(wú)法達(dá)成一致,最終從CommonJs中分裂了出去,獨(dú)立制定了瀏覽器端的js模塊化規(guī)范AMD(Asynchronous Module Definition)(https://github.com/amdjs/amdjs-api/wiki/AMD) 本文后續(xù)會(huì)繼續(xù)討論AMD規(guī)范的內(nèi)容。 ?3.?Modules/2.0派 這一波人有點(diǎn)像“中間派”,既不想丟掉舊的規(guī)范,也不想像AMD那樣推到重來(lái)。他們認(rèn)為,Modules/1.0固然不適合瀏覽器,但它里面的一些理念還是很好的,(如通過(guò)require來(lái)聲明依賴),新的規(guī)范應(yīng)該兼容這些,AMD規(guī)范也有它好的地方(例如模塊的預(yù)先加載以及通過(guò)return可以暴漏任意類型的數(shù)據(jù),而不是像commonjs那樣exports只能為object),也應(yīng)采納。最終他們制定了一個(gè)Modules/Wrappings(http://wiki.commonjs.org/wiki/Modules/Wrappings)規(guī)范,此規(guī)范指出了一個(gè)模塊應(yīng)該如何“包裝”,包含以下內(nèi)容: 1. 全局有一個(gè)module變量,用來(lái)定義模塊 2. 通過(guò)module.declare方法來(lái)定義一個(gè)模塊 3. module.declare方法只接收一個(gè)參數(shù),那就是模塊的factory,次factory可以是函數(shù)也可以是對(duì)象,如果是對(duì)象,那么模塊輸出就是此對(duì)象。 4.?模塊的factory函數(shù)傳入三個(gè)參數(shù):require,exports,module,用來(lái)引入其他依賴和導(dǎo)出本模塊API 5. 如果factory函數(shù)最后明確寫有return數(shù)據(jù)(js函數(shù)中不寫return默認(rèn)返回undefined),那么return的內(nèi)容即為模塊的輸出。 使用該規(guī)范的例子看起來(lái)像這樣: //可以使用exprots來(lái)對(duì)外暴漏API module.declare(function(require, exports, module) {exports.foo = "bar"; }); //也可以直接return來(lái)對(duì)外暴漏數(shù)據(jù) module.declare(function(require) { return { foo: "bar" }; });?
AMD/RequireJs的崛起與妥協(xié)
AMD的思想正如其名,異步加載所需的模塊,然后在回調(diào)函數(shù)中執(zhí)行主邏輯。這正是我們?cè)跒g覽器端開(kāi)發(fā)所習(xí)慣了的方式,其作者親自實(shí)現(xiàn)了符合AMD規(guī)范的requirejs,AMD/RequireJs迅速被廣大開(kāi)發(fā)者所接受。 AMD規(guī)范包含以下內(nèi)容: 1. 用全局函數(shù)define來(lái)定義模塊,用法為:define(id?, dependencies?, factory); 2. id為模塊標(biāo)識(shí),遵從CommonJS Module Identifiers規(guī)范 3.?dependencies為依賴的模塊數(shù)組,在factory中需傳入形參與之一一對(duì)應(yīng) 4. 如果dependencies的值中有"require"、"exports"或"module",則與commonjs中的實(shí)現(xiàn)保持一致 5. 如果dependencies省略不寫,則默認(rèn)為["require", "exports", "module"],factory中也會(huì)默認(rèn)傳入require,exports,module 6. 如果factory為函數(shù),模塊對(duì)外暴漏API的方法有三種:return任意類型的數(shù)據(jù)、exports.xxx=xxx、module.exports=xxx 7. 如果factory為對(duì)象,則該對(duì)象即為模塊的返回值 基于以上幾點(diǎn)基本規(guī)范,我們便可以用這樣的方式來(lái)進(jìn)行模塊化組織代碼了: //a.js define(function(){console.log('a.js執(zhí)行');return {hello: function(){console.log('hello, a.js');}} }); //b.js define(function(){console.log('b.js執(zhí)行');return {hello: function(){console.log('hello, b.js');}} }); //main.js require(['a', 'b'], function(a, b){console.log('main.js執(zhí)行');a.hello();$('#b').click(function(){b.hello();}); }) 上面的main.js被執(zhí)行的時(shí)候,會(huì)有如下的輸出: a.js執(zhí)行b.js執(zhí)行
main.js執(zhí)行
hello, a.js 在點(diǎn)擊按鈕后,會(huì)輸出: hello, b.js 這結(jié)局,如你所愿嗎?大體來(lái)看,是沒(méi)什么問(wèn)題的,因?yàn)槟阋膬蓚€(gè)hello方法都正確的執(zhí)行了。 但是如果細(xì)細(xì)來(lái)看,b.js被預(yù)先加載并且預(yù)先執(zhí)行了,(第二行輸出),b.hello這個(gè)方法是在點(diǎn)擊了按鈕之后才會(huì)執(zhí)行,如果用戶壓根就沒(méi)點(diǎn),那么b.js中的代碼應(yīng)不應(yīng)該執(zhí)行呢? 這其實(shí)也是AMD/RequireJs被吐槽的一點(diǎn),預(yù)先下載沒(méi)什么爭(zhēng)議,由于瀏覽器的環(huán)境特點(diǎn),被依賴的模塊肯定要預(yù)先下載的。問(wèn)題在于,是否需要預(yù)先執(zhí)行?如果一個(gè)模塊依賴了十個(gè)其他模塊,那么在本模塊的代碼執(zhí)行之前,要先把其他十個(gè)模塊的代碼都執(zhí)行一遍,不管這些模塊是不是馬上會(huì)被用到。這個(gè)性能消耗是不容忽視的。 另一點(diǎn)被吐槽的是,在定義模塊的時(shí)候,要把所有依賴模塊都羅列一遍,而且還要在factory中作為形參傳進(jìn)去,要寫兩遍很大一串模塊名稱,像這樣: define(['a', 'b', 'c', 'd', 'e', 'f', 'g'], function(a, b, c, d, e, f, g){ ..... }) 編碼過(guò)程略有不爽。 好的一點(diǎn)是,AMD保留了commonjs中的require、exprots、module這三個(gè)功能(上面提到的第4條)。你也可以不把依賴羅列在dependencies數(shù)組中。而是在代碼中用require來(lái)引入,如下: define(function(){console.log('main2.js執(zhí)行');require(['a'], function(a){a.hello(); });$('#b').click(function(){require(['b'], function(b){b.hello();});}); }); 我們?cè)赿efine的參數(shù)中未寫明依賴,那么main2.js在執(zhí)行的時(shí)候,就不會(huì)預(yù)先加載a.js和b.js,只是執(zhí)行到require語(yǔ)句的時(shí)候才會(huì)去加載,上述代碼的輸出如下: main2.js執(zhí)行
a.js執(zhí)行
hello, a.js 可以看到b.js并未執(zhí)行,從網(wǎng)絡(luò)請(qǐng)求中看,b.js也并未被下載。只有在按鈕被點(diǎn)擊的時(shí)候b.js才會(huì)被下載執(zhí)行,并且在回調(diào)函數(shù)中執(zhí)行模塊中的方法。這就是名副其實(shí)的“懶加載”了。 這樣的懶加載無(wú)疑會(huì)大大減輕初始化時(shí)的損耗(下載和執(zhí)行都被省去了),但是弊端也是顯而易見(jiàn)的,在后續(xù)執(zhí)行a.hello和b.hello時(shí),必須得實(shí)時(shí)下載代碼然后在回調(diào)中才能執(zhí)行,這樣的用戶體驗(yàn)是不好的,用戶的操作會(huì)有明顯的延遲卡頓。 但這樣的現(xiàn)實(shí)并非是無(wú)法接受的,畢竟是瀏覽器環(huán)境,我們已經(jīng)習(xí)慣了操作網(wǎng)頁(yè)時(shí)伴隨的各種loading。。。 但是話說(shuō)過(guò)來(lái),有沒(méi)有更好的方法來(lái)處理問(wèn)題呢?資源的下載階段還是預(yù)先進(jìn)行,資源執(zhí)行階段后置,等到需要的時(shí)候再執(zhí)行。這樣一種折衷的方式,能夠融合前面兩種方式的優(yōu)點(diǎn),而又回避了缺點(diǎn)。 這就是Modules/Wrappings規(guī)范,還記得前面提到的“中間派”嗎? 在AMD的陣營(yíng)中,也有一部分人提出這樣的觀點(diǎn),代碼里寫一堆回調(diào)實(shí)在是太惡心了,他們更喜歡這樣來(lái)使用模塊: var a = require('a'); a.hello();$('#b').click(function(){var b = require('b');b.hello(); }); 于是,AMD也終于決定作妥協(xié),兼容Modules/Wrappings的寫法,但只是部分兼容,例如并沒(méi)有使用module.declare來(lái)定義模塊,而還是用define,模塊的執(zhí)行時(shí)機(jī)也沒(méi)有改變,依舊是預(yù)先執(zhí)行。因此,AMD將此兼容稱為Simplified CommonJS wrapping,即并不是完整的實(shí)現(xiàn)Modules/Wrappings。 作了此兼容后,使用requirejs就可以這么寫代碼了: //d.js define(function(require, exports, module){console.log('d.js執(zhí)行');return {helloA: function(){var a = require('a');a.hello();},run: function(){$('#b').click(function(){var b = require('b');b.hello();});}} }); 注意定義模塊時(shí)候的輕微差異,dependencies數(shù)組為空,但是factory函數(shù)的形參必須手工寫上require,exports,module,(這不同于之前的dependencies和factory形參全不寫),這樣寫即可使用Simplified CommonJS wrapping風(fēng)格,與commonjs的格式一致了。 雖然使用上看起來(lái)簡(jiǎn)單,然而在理解上卻給后人埋下了一個(gè)大坑。因?yàn)锳MD只是支持了這樣的語(yǔ)法,而并沒(méi)有真正實(shí)現(xiàn)模塊的延后執(zhí)行。什么意思呢?上面的代碼,正常來(lái)講應(yīng)該是預(yù)先下載a.js和b.js,然后在執(zhí)行模塊的helloA方法的時(shí)候開(kāi)始執(zhí)行a.js里面的代碼,在點(diǎn)擊按鈕的時(shí)候開(kāi)始執(zhí)行b.js中的方法。實(shí)際卻不是這樣,只要此模塊被別的模塊引入,a.js和b.js中的代碼還是被預(yù)先執(zhí)行了。 我們把上面的代碼命名為d.js,在別的地方使用它: require(['d'], function(d){}); 上面的代碼會(huì)輸出 a.js執(zhí)行
b.js執(zhí)行
d.js執(zhí)行 可以看出,盡管還未調(diào)用d模塊的API,里面所依賴的a.js和b.js中的代碼已經(jīng)執(zhí)行了。AMD的這種只實(shí)現(xiàn)語(yǔ)法卻未真正實(shí)現(xiàn)功能的做法容易給人造成理解上的困難,被強(qiáng)烈吐槽。 (在requirejs2.0中,作者聲明已經(jīng)處理了此問(wèn)題(https://github.com/jrburke/requirejs/wiki/Upgrading-to-RequireJS-2.0#delayed),但是我用2.1.20版測(cè)試的時(shí)候還是會(huì)預(yù)先執(zhí)行,我有點(diǎn)不太明白原因,如果有懂的高手請(qǐng)指教)
?
兼容并包的CMD/seajs
既然requirejs有上述種種不甚優(yōu)雅的地方,所以必然會(huì)有新東西來(lái)完善它,這就是后起之秀seajs,seajs的作者是國(guó)內(nèi)大牛淘寶前端布道者玉伯。seajs全面擁抱Modules/Wrappings規(guī)范,不用requirejs那樣回調(diào)的方式來(lái)編寫模塊。而它也不是完全按照Modules/Wrappings規(guī)范,seajs并沒(méi)有使用declare來(lái)定義模塊,而是使用和requirejs一樣的define,或許作者本人更喜歡這個(gè)名字吧。(然而這或多或少又會(huì)給人們?cè)斐衫斫馍系幕煜?#xff09;,用seajs定義模塊的寫法如下: //a.js define(function(require, exports, module){console.log('a.js執(zhí)行');return {hello: function(){console.log('hello, a.js');}} }); //b.js define(function(require, exports, module){console.log('b.js執(zhí)行');return {hello: function(){console.log('hello, b.js');}} }); //main.js define(function(require, exports, module){console.log('main.js執(zhí)行');var a = require('a');a.hello(); $('#b').click(function(){var b = require('b');b.hello();});}); 定義模塊時(shí)無(wú)需羅列依賴數(shù)組,在factory函數(shù)中需傳入形參require,exports,module,然后它會(huì)調(diào)用factory函數(shù)的toString方法,對(duì)函數(shù)的內(nèi)容進(jìn)行正則匹配,通過(guò)匹配到的require語(yǔ)句來(lái)分析依賴,這樣就真正實(shí)現(xiàn)了commonjs風(fēng)格的代碼。 上面的main.js執(zhí)行會(huì)輸出如下: main.js執(zhí)行a.js執(zhí)行
hello, a.js a.js和b.js都會(huì)預(yù)先下載,但是b.js中的代碼卻沒(méi)有執(zhí)行,因?yàn)檫€沒(méi)有點(diǎn)擊按鈕。當(dāng)點(diǎn)擊按鈕的時(shí)候,會(huì)輸出如下: b.js執(zhí)行
hello, b.js 可以看到b.js中的代碼此時(shí)才執(zhí)行。這樣就真正實(shí)現(xiàn)了“就近書(shū)寫,延遲執(zhí)行“,不可謂不優(yōu)雅。 如果你一定要挑出一點(diǎn)不爽的話,那就是b.js的預(yù)先下載了。你可能不太想一開(kāi)始就下載好所有的資源,希望像requirejs那樣,等點(diǎn)擊按鈕的時(shí)候再開(kāi)始下載b.js。本著兼容并包的思想,seajs也實(shí)現(xiàn)了這一功能,提供require.async API,在點(diǎn)擊按鈕的時(shí)候,只需這樣寫: var b = require.async('b'); b.hello(); b.js就不會(huì)在一開(kāi)始的時(shí)候就加載了。這個(gè)API可以說(shuō)是簡(jiǎn)單漂亮。 關(guān)于模塊對(duì)外暴漏API的方式,seajs也是融合了各家之長(zhǎng),支持commonjs的exports.xxx = xxx和module.exports = xxx的寫法,也支持AMD的return寫法,暴露的API可以是任意類型。 你可能會(huì)覺(jué)得seajs無(wú)非就是一個(gè)抄,把別人家的優(yōu)點(diǎn)都抄過(guò)來(lái)組合了一下。其實(shí)不然,seajs是commonjs規(guī)范在瀏覽器端的踐行者,對(duì)于requirejs的優(yōu)點(diǎn)也加以吸收。看人家的名字,就是海納百川之意。(再論起名的重要性~),既然它的思想是海納百川,討論是不是抄就沒(méi)意義了。 鑒于seajs融合了太多的東西,已經(jīng)無(wú)法說(shuō)它遵循哪個(gè)規(guī)范了,所以玉伯干脆就自立門戶,起名曰CMD(Common Module Definition)規(guī)范,有了綱領(lǐng),就不會(huì)再存在非議了。
?
面向未來(lái)的ES6模塊標(biāo)準(zhǔn)
既然模塊化開(kāi)發(fā)的呼聲這么高,作為官方的ECMA必然要有所行動(dòng),js模塊很早就列入草案,終于在2015年6月份發(fā)布了ES6正式版。然而,可能由于所涉及的技術(shù)還未成熟,ES6移除了關(guān)于模塊如何加載/執(zhí)行的內(nèi)容,只保留了定義、引入模塊的語(yǔ)法。所以說(shuō)現(xiàn)在的ES6 Module還只是個(gè)雛形,半成品都算不上。但是這并不妨礙我們先窺探一下ES6模塊標(biāo)準(zhǔn)。 定義一個(gè)模塊不需要專門的工作,因?yàn)橐粋€(gè)模塊的作用就是對(duì)外提供API,所以只需用exoprt導(dǎo)出就可以了: //方式一, a.js export var a = 1; export var obj = {name: 'abc', age: 20}; export function run(){....} //方式二, b.js var a = 1; var obj = {name: 'abc', age: 20}; function run(){....} export {a, obj, run} 使用模塊的時(shí)候用import關(guān)鍵字,如: import {run as go} from 'a' run() 如果想要使用模塊中的全部API,也可以不必把每個(gè)都列一遍,使用module關(guān)鍵字可以全部引入,用法: module foo from 'a' console.log(foo.obj); a.run(); 在花括號(hào)中指明需使用的API,并且可以用as指定別名。 ES6 Module的基本用法就是這樣,可以看到確實(shí)是有些薄弱,而且目前還沒(méi)有瀏覽器能支持,只能說(shuō)它是面向未來(lái)了。 目前我們可以使用一些第三方模塊來(lái)對(duì)ES6進(jìn)行編譯,轉(zhuǎn)化為可以使用的ES5代碼,或者是符合AMD規(guī)范的模塊,例如ES6 module transpiler。另外有一個(gè)項(xiàng)目也提供了加載ES6模塊的方法,es6-module-loader(https://github.com/ModuleLoader/es6-module-loader),不過(guò)這都是一些臨時(shí)的方案,或許明年ES7一發(fā)布,模塊的加載有了標(biāo)準(zhǔn),瀏覽器給與了實(shí)現(xiàn),這些工具也就沒(méi)有用武之地了。 未來(lái)還是很值得期待的,從語(yǔ)言的標(biāo)準(zhǔn)上支持模塊化,js就可以更加自信的走進(jìn)大規(guī)模企業(yè)級(jí)開(kāi)發(fā)。 ======================= 參考資料: https://github.com/seajs/seajs/issues/588 http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition http://www.cnblogs.com/snandy/archive/2012/03/12/2390782.html http://www.cnblogs.com/snandy/archive/2012/03/30/2423612.html https://imququ.com/post/amd-simplified-commonjs-wrapping.html https://github.com/jrburke/requirejs/wiki/Upgrading-to-RequireJS-2.0#delayed轉(zhuǎn)載于:https://www.cnblogs.com/cnblogs-jcy/p/6903061.html
總結(jié)
- 上一篇: 【剑指Offer】俯视50题之31 -
- 下一篇: WebForm(文件上传)