這次是前端精讀期刊與大家第一次正式碰面央渣,我們每周會精讀并分析若干篇精品好文计盒,試圖討論出結(jié)論性觀點(diǎn)。沒錯(cuò)芽丹,我們試圖通過觀點(diǎn)的碰撞北启,爭做無主觀精品好文的意見領(lǐng)袖。
我是這一期的主持人 —— 黃子毅
本期精讀的文章是:evolutionOfJsModularity拔第。
懶得看文章咕村?沒關(guān)系,稍后會附上文章內(nèi)容概述蚊俺,同時(shí)懈涛,更希望能通過閱讀這一期的精讀,穿插著深入閱讀原文泳猬。
1 引言
<img src="assets/1/cube.jpeg" alt="logo" width="500" />
如今批钠,Javascript 模塊化規(guī)范非常方便、自然得封,但這個(gè)新規(guī)范僅執(zhí)行了2年埋心,就在 4 年前,js 的模塊化還停留在運(yùn)行時(shí)支持忙上,10 年前拷呆,通過后端模版定義、注釋定義模塊依賴晨横。對經(jīng)歷過來的人來說洋腮,歷史的模塊化方式還停留在腦海中,反而新上手的同學(xué)會更快接受現(xiàn)代的模塊化規(guī)范手形。
但為什么要了解 Javascript 模塊化發(fā)展的歷史呢啥供?因?yàn)榉彩露加袃擅嫘裕私?Javascript 模塊化規(guī)范库糠,有利于我們思考出更好的模塊化方案伙狐,縱觀歷史,從 1999 年開始瞬欧,模塊化方案最多維持兩年贷屎,就出現(xiàn)了新的替代方案,比原有的模塊化更清晰艘虎、強(qiáng)壯唉侄,我們不能被現(xiàn)代模塊化方式限制住思維,因?yàn)楝F(xiàn)在的 ES2015 模塊化方案距離發(fā)布也僅僅過了兩年野建。
2 內(nèi)容概要
直接定義依賴 (1999): 由于當(dāng)時(shí) js 文件非常簡單属划,模塊化方式非常簡單粗暴 —— 通過全局方法定義恬叹、引用模塊。這種定義方式與現(xiàn)在的 commonjs 非常神似同眯,區(qū)別是 commonjs 以文件作為模塊绽昼,而這種方法可以在任何文件中定義模塊,模塊不與文件關(guān)聯(lián)须蜗。
閉包模塊化模式 (2003): 用閉包方式解決了變量污染問題硅确,閉包內(nèi)返回模塊對象,只需對外暴露一個(gè)全局變量明肮。
模版依賴定義 (2006): 這時(shí)候開始流行后端模版語法菱农,通過后端語法聚合 js 文件,從而實(shí)現(xiàn)依賴加載柿估,說實(shí)話大莫,現(xiàn)在 go 語言等模版語法也很流行這種方式,寫后端代碼的時(shí)候不覺得官份,回頭看看只厘,還是掛在可維護(hù)性上。
注釋依賴定義 (2006): 幾乎和模版依賴定義同時(shí)出現(xiàn)舅巷,與 1999 年方案不同的羔味,不僅僅是模塊定義方式,而是終于以文件為單位定義模塊了钠右,通過 lazyjs 加載文件赋元,同時(shí)讀取文件注釋,繼續(xù)遞歸加載剩下的文件飒房。
外部依賴定義 (2007): 這種定義方式在 cocos2d-js 開發(fā)中普遍使用搁凸,其核心思想是將依賴抽出單獨(dú)文件定義,這種方式不利于項(xiàng)目管理狠毯,畢竟依賴抽到代碼之外护糖,我是不是得兩頭找呢?所以才有通過 webpack 打包為一個(gè)文件的方式暴力替換為 commonjs 的方式出現(xiàn)嚼松。
Sandbox模式 (2009): 這種模塊化方式很簡單嫡良,暴力,將所有模塊塞到一個(gè) sanbox
變量中献酗,硬傷是無法解決明明沖突問題寝受,畢竟都塞到一個(gè) sandbox
對象里,而 Sandbox
對象也需要定義在全局罕偎,存在被覆蓋的風(fēng)險(xiǎn)很澄。模塊化需要保證全局變量盡量干凈,目前為止的模塊化方案都沒有很好的做到這一點(diǎn)。
依賴注入 (2009): 就是大家熟知的 angular1.0甩苛,依賴注入的思想現(xiàn)在已廣泛運(yùn)用在 react忙干、vue 等流行框架中。但依賴注入和解決模塊化問題還差得遠(yuǎn)。
CommonJS (2009): 真正解決模塊化問題,從 node 端逐漸發(fā)力到前端卢厂,前端需要使用構(gòu)建工具模擬嗡呼。
Amd (2009): 都是同一時(shí)期的產(chǎn)物,這個(gè)方案主要解決前端動(dòng)態(tài)加載依賴反浓,相比 commonJs萌丈,體積更小,按需加載雷则。
Umd (2011): 兼容了 CommonJS 與 Amd辆雾,其核心思想是,如果在 commonjs 環(huán)境(存在 module.exports
月劈,不存在 define
)度迂,將函數(shù)執(zhí)行結(jié)果交給 module.exports
實(shí)現(xiàn) Commonjs,否則用 Amd 環(huán)境的 define
猜揪,實(shí)現(xiàn) Amd惭墓。
Labeled Modules (2012): 和 Commonjs 很像了,沒什么硬傷而姐,但生不逢時(shí)腊凶,碰上 Commonjs 與 Amd,那只有被人遺忘的份了拴念。
YModules (2013): 既然都出了 Commonjs Amd钧萍,文章還列出了此方案,一定有其獨(dú)到之處政鼠。其核心思想在于使用 provide
取代 return
风瘦,可以控制模塊結(jié)束時(shí)機(jī),處理異步結(jié)果公般;拿到第二個(gè)參數(shù) module
弛秋,修改其他模塊的定義(雖然很有拓展性,但用在項(xiàng)目里是個(gè)攪屎棍)俐载。
ES2015 Modules (2015): 就是我們現(xiàn)在的模塊化方案蟹略,還沒有被瀏覽器實(shí)現(xiàn),大部分項(xiàng)目已通過 babel
或 typescript
提前體驗(yàn)遏佣。
3 精讀
本次提出獨(dú)到觀點(diǎn)的同學(xué)有:流形挖炬,黃子毅,蘇里約状婶,camsong意敛,楊森馅巷,淡蒼,留影草姻,精讀由此歸納钓猬。
從語言層面到文件層面的模塊化
從 1999 年開始,模塊化探索都是基于語言層面的優(yōu)化撩独,真正的革命從 2009 年 CommonJS 的引入開始敞曹,前端開始大量使用預(yù)編譯。
這篇文章所提供的模塊化歷史的方案都是邏輯模塊化综膀,從 CommonJS 方案開始前端把服務(wù)端的解決方案搬過來之后澳迫,算是看到標(biāo)準(zhǔn)物理與邏輯統(tǒng)一的模塊化。但之后前端工程不得不引入模塊化構(gòu)建這一步剧劝。正是這一步給前端開發(fā)無疑帶來了諸多的不便橄登,尤其是現(xiàn)在我們開發(fā)過程中經(jīng)常為了優(yōu)化這個(gè)工具帶了很多額外的成本。
從 CommonJS 之前其實(shí)都只是封裝讥此,并沒有一套模塊化規(guī)范拢锹,這個(gè)就有些像類與包的概念。我在10年左右用的最多的還是 YUI2萄喳,YUI2 是用 namespace 來做模塊化的面褐,但有很多問題沒有解決,比如多版本共存取胎,因此后來 YUI3 出來了展哭。
YUI().use('node', 'event', function (Y) {
// The Node and Event modules are loaded and ready to use.
// Your code goes here!
});
YUI3 的 sandbox 像極了差不多同時(shí)出現(xiàn)的 AMD 規(guī)范,但早期 yahoo 在前端圈的影響力還是很大的闻蛀,而 requirejs 到 2011 年才誕生匪傍,因此圈子不是用著 YUI 要不就自己封裝一套 sandbox,內(nèi)部使用 jQuery觉痛。
為什么模塊化方案這么晚才成型役衡,可能早期應(yīng)用的復(fù)雜度都在后端,前端都是非常簡單邏輯薪棒。后來 Ajax 火了之后手蝎,web app 概念的開始流行,前端的復(fù)雜度也呈指數(shù)級上漲俐芯,到今天幾乎和后端接近一個(gè)量級棵介。工程發(fā)展到一定階段,要出現(xiàn)的必然會出現(xiàn)吧史。
前端三劍客的模塊化展望
從 js 模塊化發(fā)展史邮辽,我們還看到了 css html 模塊化方面的嚴(yán)重落后,如今依賴編譯工具的模塊化增強(qiáng)在未來會被標(biāo)準(zhǔn)所替代。
原生支持的模塊化吨述,解決 html 與 css 模塊化問題正是以后的方向岩睁。
再回到 JS 模塊化這個(gè)主題,開頭也說到是為了構(gòu)建 scope揣云,實(shí)則提供了業(yè)務(wù)規(guī)范標(biāo)準(zhǔn)的輸入輸出的方式捕儒。但文章中的 JS 的模塊化還不等于前端工程的模塊化,Web 界面是由 HTML邓夕、CSS 和 JS 三種語言實(shí)現(xiàn)刘莹,不論是 CommonJS 還是 AMD 包括之后的方案都無法解決 CSS 與 HTML 模塊化的問題。
對于 CSS 本身它就是 global scope翎迁,因此開發(fā)樣式可以說是喜憂參半。近幾年也涌現(xiàn)把 HTML净薛、CSS 和 JS 合并作模塊化的方案汪榔,其中 react/css-modules 和 vue 都為人熟知。當(dāng)然肃拜,這一點(diǎn)還是非常依賴于 webpack/rollup 等構(gòu)建工具痴腌,讓我們意識到在 browser 端還有很多本質(zhì)的問題需要推進(jìn)。
對于 css 模塊化燃领,目前不依賴預(yù)編譯的方式是 styled-component
士聪,通過 js 動(dòng)態(tài)創(chuàng)建 class。而目前 css 也引入了與 js 通信的機(jī)制 與 原生變量支持猛蔽。未來 css 模塊化也很可能是運(yùn)行時(shí)的剥悟,所以目前比較看好 styled-component
的方向。
對于 html 模塊化曼库,小尤最近爆出與 chrome 小組調(diào)研 html Modules区岗,如果 html 得到了瀏覽器,編輯器的模塊化支持毁枯,未來可能會取代 jsx 成為最強(qiáng)大的模塊化慈缔、模板語言。
對于 js 模塊化种玛,最近出現(xiàn)的 <script type="module">
方式藐鹤,雖然還沒有得到瀏覽器原生支持,但也是我比較看好的未來趨勢赂韵,這樣就連 webpack 的拆包都不需要了娱节,直接把源代碼傳到服務(wù)器,配合 http2.0 完美拋開預(yù)編譯的枷鎖祭示。
上述三中方案都不依賴預(yù)編譯括堤,分別實(shí)現(xiàn)了 html、css、js 模塊化悄窃,相信這就是未來讥电。
模塊化標(biāo)準(zhǔn)推進(jìn)速度仍然緩慢
2015 年提出的標(biāo)準(zhǔn),在 17 年依然沒有得到實(shí)現(xiàn)轧抗,即便在 nodejs 端恩敌。
這幾年 TC39 對語言終于重視起來了,慢慢有動(dòng)作了横媚,但針對模塊標(biāo)準(zhǔn)制定的速度纠炮,與落實(shí)都非常緩慢,與 javascript 越來越流行的趨勢逐漸脫節(jié)灯蝴。nodejs 至今也沒有實(shí)現(xiàn) ES2015 模塊化規(guī)范恢口,所有 jser 都處在構(gòu)建工具的陰影下。
Http 2.0 對 js 模塊化的推動(dòng)
js 模塊化定義的再美好穷躁,瀏覽器端的支持粒度永遠(yuǎn)是瓶頸耕肩,http 2.0 正是考慮到了這個(gè)因素,大力支持了 ES 2015 模塊化規(guī)范问潭。
幸運(yùn)的是猿诸,模塊化構(gòu)建將來可能不再需要。隨著 HTTP/2 流行起來狡忙,請求和響應(yīng)可以并行梳虽,一次連接允許多個(gè)請求,對于前端來說宣告不再需要在開發(fā)和上線時(shí)再做編譯這個(gè)動(dòng)作灾茁。
幾年前窜觉,模塊化幾乎是每個(gè)流行庫必造的輪子(YUI、Dojo北专、Angular)竖螃,大牛們自己爽的同時(shí)其實(shí)造成了社區(qū)的分裂,很難積累逗余。有了 ES2015 Modules 之后特咆,JS 開發(fā)者終于可以像 Java 開始者十年前一樣使用一致的方式愉快的互相引用模塊。
不過 ES2015 Modules 也只是解決了開發(fā)的問題录粱,由于瀏覽器的特殊性腻格,還是要經(jīng)過繁瑣打包的過程,等 Import啥繁,Export 和 HTTP 2.0 被主流瀏覽器支持菜职,那時(shí)候才是徹底的模塊化。
Http 2.0 后就不需要構(gòu)建工具了嗎旗闽?
看到大家基本都提到了 HTTP/2酬核,對這項(xiàng)技術(shù)解決前端模塊化及資源打包等工程問題抱有非常大的期待蜜另。很多人也認(rèn)為 HTTP/2 普及后,基本就沒有 Webpack 什么事情了嫡意。
不過 Webpack 作者 @sokra 在他的文章 webpack & HTTP/2 里提到了一個(gè)新的 Webpack 插件 AggressiveSplittingPlugin
举瑰。簡單的說,這款插件就是為了充分利用 HTTP/2 的文件緩存能力蔬螟,將你的業(yè)務(wù)代碼自動(dòng)拆分成若干個(gè)數(shù)十 KB 的小文件此迅。后續(xù)若其中任意一個(gè)文件發(fā)生變化,可以保證其他的小 chunck 不需要重新下載旧巾。
可見耸序,即使不斷的有新技術(shù)出現(xiàn),也依然需要配套的工具來將前端工程問題解決方案推向極致鲁猩。
模塊化是大型項(xiàng)目的銀彈嗎坎怪?
只要遵循了最新模塊化規(guī)范,就可以使項(xiàng)目具有最好的可維護(hù)性嗎廓握? Js 模塊化的目的是支持前端日益上升的復(fù)雜度搅窿,但絕不是唯一的解決方案。
分析下 JavaScript 為什么沒有模塊化疾棵,為什么又需要模塊化:這個(gè) 95 年被設(shè)計(jì)出來的時(shí)候戈钢,語言的開發(fā)者根本沒有想到它會如此的大放異彩痹仙,也沒有將它設(shè)計(jì)成一種模塊化語言是尔。按照文中的說法,99 年也就是 4 年后開始出現(xiàn)了模塊化的需求开仰。如果只有幾行代碼用模塊化是扯拟枚,初始的 web 開發(fā)業(yè)務(wù)邏輯都寫在 server 端,js 的作用小之又小众弓。而現(xiàn)在 spa 都出現(xiàn)了恩溅,幾乎所有的渲染邏輯都在前端,如果還是沒有模塊化的組織谓娃,開發(fā)過程會越來越難脚乡,維護(hù)也是更痛苦。
文中已經(jīng)詳細(xì)說明了模塊化的發(fā)展和優(yōu)劣滨达,這里不準(zhǔn)備做過多的討論奶稠。我想說的是,在模塊化之后還有一個(gè)模塊間耦合的問題捡遍,如果模塊間耦合度大也會降低代碼的可重用性或者說復(fù)用性锌订。所以也出現(xiàn)了降低耦合的觀察者模式或者發(fā)布/訂閱模式。這對于提升代碼重用画株,復(fù)用性和避免單點(diǎn)故障等都很重要辆飘。說到這里啦辐,還想順便提一下最近流行起來的響應(yīng)式編程(RxJS),響應(yīng)式編程中有一個(gè)很核心的概念就是 observable蜈项,也就是 Rx 中的流(stream)芹关。它可以被 subscribe,其實(shí)也就是觀察者設(shè)計(jì)模式战得。
補(bǔ)充閱讀
總結(jié)
未來前端復(fù)雜度不斷增加已成定論充边,隨著后端成熟,自然會將焦點(diǎn)轉(zhuǎn)移到前端領(lǐng)域常侦,而且服務(wù)化浇冰、用戶體驗(yàn)越來越重要,前端體驗(yàn)早不是當(dāng)初能看就行聋亡,任何網(wǎng)頁的異常肘习、視覺的差異,或文案的模糊坡倔,都會導(dǎo)致用戶流失漂佩,支付中斷。前端對公司營收的影響罪塔,漸漸與后端服務(wù)宕機(jī)同等嚴(yán)重投蝉,所以前端會越來越重,異常監(jiān)控征堪,性能檢測瘩缆,工具鏈,可視化等等都是這幾年大家逐漸重視起來的佃蚜。
我們早已不能將 javascript 早期玩具性質(zhì)的模塊化方案用于現(xiàn)代越來越重要的系統(tǒng)中庸娱,前端界必然出現(xiàn)同等重量級的模塊化管理方案,感謝 TC39 制定的 ES2015 模塊化規(guī)范谐算,我們已經(jīng)離不開它熟尉,哪怕所有人必須使用 babel。
話說回來洲脂,標(biāo)準(zhǔn)推進(jìn)的太慢斤儿,我們還是把編譯工具當(dāng)作常態(tài),抱著哪怕支持了 ES2015 所有特性恐锦,babel 依然還有用的心態(tài)往果,將預(yù)編譯進(jìn)行到底。一句話踩蔚,模塊化仍在路上棚放。js 模塊化的矛頭已經(jīng)對準(zhǔn)了 css 與 html,這兩位元老也該向前衛(wèi)的 js 學(xué)習(xí)學(xué)習(xí)了馅闽。
未來 css飘蚯、html 的模塊化會自立門戶馍迄,還是賦予 js 更強(qiáng)的能力,讓兩者的模塊化依附于 js 的能力呢局骤?目前 html 有自立門戶的苗頭(htmlModules)攀圈,而 css 遲遲沒有改變,社區(qū)出現(xiàn)的 styled-component
已經(jīng)用 js 將 css 模塊化得很好了峦甩,最新 css 規(guī)范也支持了與 js 的變量通信赘来,難道希望依附于 js 嗎?這里希望得到大家更廣泛的討論凯傲。
我也認(rèn)同犬辰,畢竟壓縮、混淆冰单、md5幌缝、或者利用 nonce 屬性對 script 標(biāo)簽加密,都離不開本地構(gòu)建工具诫欠。
據(jù)說 http2 的優(yōu)化中涵卵,有個(gè)最佳文件大小與數(shù)量的比例,那么還是脫離不了構(gòu)建工具荒叼,前端未來會越來越復(fù)雜轿偎,同時(shí)也越來越美好。
至此被廓,對于 javascript 模塊化討論已接近尾聲坏晦,對其優(yōu)缺點(diǎn)也基本達(dá)成了一致。前端復(fù)雜度不斷提高伊者,促使著模塊化的改進(jìn)英遭,代理(瀏覽器间护、node) 的支持程度亦渗,與前端特殊性(流量、緩存)可能前端永遠(yuǎn)也離不開構(gòu)建工具汁尺,新的標(biāo)準(zhǔn)會讓這些工作做的更好法精,同時(shí)取代、增強(qiáng)部分特征痴突,前端的未來是更加美好的搂蜓,復(fù)雜度也更高。
如果你想?yún)⑴c討論辽装,請點(diǎn)擊這里帮碰,每周都有新的主題,每周五發(fā)布拾积。