精讀《js 模塊化發展》

此次是前端精讀期刊與你們第一次正式碰面,咱們每週會精讀並分析若干篇精品好文,試圖討論出結論性觀點。沒錯,咱們試圖經過觀點的碰撞,爭作無主觀精品好文的意見領袖。javascript

我是這一期的主持人 —— 黃子毅css

本期精讀的文章是:evolutionOfJsModularityhtml

懶得看文章?不要緊,稍後會附上文章內容概述,同時,更但願能經過閱讀這一期的精讀,穿插着深刻閱讀原文。前端

1 引言

現在,Javascript 模塊化規範很是方便、天然,但這個新規範僅執行了2年,就在 4 年前,js 的模塊化還停留在運行時支持,10 年前,經過後端模版定義、註釋定義模塊依賴。對經歷過來的人來講,歷史的模塊化方式還停留在腦海中,反而新上手的同窗會更快接受現代的模塊化規範。

但爲何要了解 Javascript 模塊化發展的歷史呢?由於凡事都有兩面性,瞭解 Javascript 模塊化規範,有利於咱們思考出更好的模塊化方案,縱觀歷史,從 1999 年開始,模塊化方案最多維持兩年,就出現了新的替代方案,比原有的模塊化更清晰、強壯,咱們不能被現代模塊化方式限制住思惟,由於如今的 ES2015 模塊化方案距離發佈也僅僅過了兩年。vue

2 內容概要

直接定義依賴 (1999): 因爲當時 js 文件很是簡單,模塊化方式很是簡單粗暴 —— 經過全局方法定義、引用模塊。這種定義方式與如今的 commonjs 很是神似,區別是 commonjs 以文件做爲模塊,而這種方法能夠在任何文件中定義模塊,模塊不與文件關聯。java

閉包模塊化模式 (2003): 用閉包方式解決了變量污染問題,閉包內返回模塊對象,只需對外暴露一個全局變量。node

模版依賴定義 (2006): 這時候開始流行後端模版語法,經過後端語法聚合 js 文件,從而實現依賴加載,說實話,如今 go 語言等模版語法也很流行這種方式,寫後端代碼的時候不以爲,回頭看看,仍是掛在可維護性上。react

註釋依賴定義 (2006): 幾乎和模版依賴定義同時出現,與 1999 年方案不一樣的,不只僅是模塊定義方式,而是終於以文件爲單位定義模塊了,經過 lazyjs 加載文件,同時讀取文件註釋,繼續遞歸加載剩下的文件。webpack

外部依賴定義 (2007): 這種定義方式在 cocos2d-js 開發中廣泛使用,其核心思想是將依賴抽出單獨文件定義,這種方式不利於項目管理,畢竟依賴抽到代碼以外,我是否是得兩頭找呢?因此纔有經過 webpack 打包爲一個文件的方式暴力替換爲 commonjs 的方式出現。git

Sandbox模式 (2009): 這種模塊化方式很簡單,暴力,將全部模塊塞到一個 sanbox 變量中,硬傷是沒法解決明明衝突問題,畢竟都塞到一個 sandbox 對象裏,而 Sandbox 對象也須要定義在全局,存在被覆蓋的風險。模塊化須要保證全局變量儘可能乾淨,目前爲止的模塊化方案都沒有很好的作到這一點。

依賴注入 (2009): 就是你們熟知的 angular1.0,依賴注入的思想如今已普遍運用在 react、vue 等流行框架中。但依賴注入和解決模塊化問題還差得遠。

CommonJS (2009): 真正解決模塊化問題,從 node 端逐漸發力到前端,前端須要使用構建工具模擬。

Amd (2009): 都是同一時期的產物,這個方案主要解決前端動態加載依賴,相比 commonJs,體積更小,按需加載。

Umd (2011): 兼容了 CommonJS 與 Amd,其核心思想是,若是在 commonjs 環境(存在 module.exports,不存在 define),將函數執行結果交給 module.exports 實現 Commonjs,不然用 Amd 環境的 define,實現 Amd。

Labeled Modules (2012): 和 Commonjs 很像了,沒什麼硬傷,但生不逢時,碰上 Commonjs 與 Amd,那只有被人遺忘的份了。

YModules (2013): 既然都出了 Commonjs Amd,文章還列出了此方案,必定有其獨到之處。其核心思想在於使用 provide 取代 return,能夠控制模塊結束時機,處理異步結果;拿到第二個參數 module,修改其餘模塊的定義(雖然頗有拓展性,但用在項目裏是個攪屎棍)。

ES2015 Modules (2015): 就是咱們如今的模塊化方案,尚未被瀏覽器實現,大部分項目已經過 babeltypescript 提早體驗。

3 精讀

本次提出獨到觀點的同窗有:流形黃子毅蘇里約camsong楊森淡蒼留影,精讀由此概括。

從語言層面到文件層面的模塊化

從 1999 年開始,模塊化探索都是基於語言層面的優化,真正的革命從 2009 年 CommonJS 的引入開始,前端開始大量使用預編譯。

這篇文章所提供的模塊化歷史的方案都是邏輯模塊化,從 CommonJS 方案開始前端把服務端的解決方案搬過來以後,算是看到標準物理與邏輯統一的模塊化。但以後前端工程不得不引入模塊化構建這一步。正是這一步給前端開發無疑帶來了諸多的不便,尤爲是如今咱們開發過程當中常常爲了優化這個工具帶了不少額外的成本。

從 CommonJS 以前其實都只是封裝,並無一套模塊化規範,這個就有些像類與包的概念。我在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 像極了差很少同時出現的 AMD 規範,但早期 yahoo 在前端圈的影響力仍是很大的,而 requirejs 到 2011 年才誕生,所以圈子不是用着 YUI 要不就本身封裝一套 sandbox,內部使用 jQuery。

爲何模塊化方案這麼晚才成型,可能早期應用的複雜度都在後端,前端都是很是簡單邏輯。後來 Ajax 火了以後,web app 概念的開始流行,前端的複雜度也呈指數級上漲,到今天幾乎和後端接近一個量級。工程發展到必定階段,要出現的必然會出現。
 

前端三劍客的模塊化展望

從 js 模塊化發展史,咱們還看到了 css html 模塊化方面的嚴重落後,現在依賴編譯工具的模塊化加強在將來會被標準所替代。

原生支持的模塊化,解決 html 與 css 模塊化問題正是之後的方向。

再回到 JS 模塊化這個主題,開頭也說到是爲了構建 scope,實則提供了業務規範標準的輸入輸出的方式。但文章中的 JS 的模塊化還不等於前端工程的模塊化,Web 界面是由 HTML、CSS 和 JS 三種語言實現,不管是 CommonJS 仍是 AMD 包括以後的方案都沒法解決 CSS 與 HTML 模塊化的問題。

對於 CSS 自己它就是 global scope,所以開發樣式能夠說是喜憂參半。近幾年也涌現把 HTML、CSS 和 JS 合併做模塊化的方案,其中 react/css-modules 和 vue 都爲人熟知。固然,這一點仍是很是依賴於 webpack/rollup 等構建工具,讓咱們意識到在 browser 端還有不少本質的問題須要推動。

對於 css 模塊化,目前不依賴預編譯的方式是 styled-component,經過 js 動態建立 class。而目前 css 也引入了與 js 通訊的機制 與 原生變量支持。將來 css 模塊化也極可能是運行時的,因此目前比較看好 styled-component 的方向。

對於 html 模塊化,小尤最近爆出與 chrome 小組調研 html Modules,若是 html 獲得了瀏覽器,編輯器的模塊化支持,將來可能會取代 jsx 成爲最強大的模塊化、模板語言。

對於 js 模塊化,最近出現的 <script type="module"> 方式,雖然尚未獲得瀏覽器原生支持,但也是我比較看好的將來趨勢,這樣就連 webpack 的拆包都不須要了,直接把源代碼傳到服務器,配合 http2.0 完美拋開預編譯的枷鎖。

上述三中方案都不依賴預編譯,分別實現了 html、css、js 模塊化,相信這就是將來。

模塊化標準推動速度仍然緩慢

2015 年提出的標準,在 17 年依然沒有獲得實現,即使在 nodejs 端。

這幾年 TC39 對語言終於重視起來了,慢慢有動做了,但針對模塊標準制定的速度,與落實都很是緩慢,與 javascript 愈來愈流行的趨勢逐漸脫節。nodejs 至今也沒有實現 ES2015 模塊化規範,全部 jser 都處在構建工具的陰影下。

Http 2.0 對 js 模塊化的推進

js 模塊化定義的再美好,瀏覽器端的支持粒度永遠是瓶頸,http 2.0 正是考慮到了這個因素,大力支持了 ES 2015 模塊化規範。

幸運的是,模塊化構建未來可能再也不須要。隨着 HTTP/2 流行起來,請求和響應能夠並行,一次鏈接容許多個請求,對於前端來講宣告再也不須要在開發和上線時再作編譯這個動做。

幾年前,模塊化幾乎是每一個流行庫必造的輪子(YUI、Dojo、Angular),大牛們本身爽的同時其實形成了社區的分裂,很難積累。有了 ES2015 Modules 以後,JS 開發者終於能夠像 Java 開始者十年前同樣使用一致的方式愉快的互相引用模塊。

不過 ES2015 Modules 也只是解決了開發的問題,因爲瀏覽器的特殊性,仍是要通過繁瑣打包的過程,等 Import,Export 和 HTTP 2.0 被主流瀏覽器支持,那時候纔是完全的模塊化。

Http 2.0 後就不須要構建工具了嗎?

看到你們基本都提到了 HTTP/2,對這項技術解決前端模塊化及資源打包等工程問題抱有很是大的期待。不少人也認爲 HTTP/2 普及後,基本就沒有 Webpack 什麼事情了。

不過 Webpack 做者 @sokra 在他的文章 webpack & HTTP/2 裏提到了一個新的 Webpack 插件 AggressiveSplittingPlugin。簡單的說,這款插件就是爲了充分利用 HTTP/2 的文件緩存能力,將你的業務代碼自動拆分紅若干個數十 KB 的小文件。後續若其中任意一個文件發生變化,能夠保證其餘的小 chunck 不須要從新下載。

可見,即便不斷的有新技術出現,也依然須要配套的工具來將前端工程問題解決方案推向極致。

模塊化是大型項目的銀彈嗎?

只要遵循了最新模塊化規範,就可使項目具備最好的可維護性嗎? Js 模塊化的目的是支持前端日益上升的複雜度,但毫不是惟一的解決方案。

分析下 JavaScript 爲何沒有模塊化,爲何又須要模塊化:這個 95 年被設計出來的時候,語言的開發者根本沒有想到它會如此的大放異彩,也沒有將它設計成一種模塊化語言。按照文中的說法,99 年也就是 4 年後開始出現了模塊化的需求。若是隻有幾行代碼用模塊化是扯,初始的 web 開發業務邏輯都寫在 server 端,js 的做用小之又小。而如今 spa 都出現了,幾乎全部的渲染邏輯都在前端,若是仍是沒有模塊化的組織,開發過程會愈來愈難,維護也是更痛苦。

文中已經詳細說明了模塊化的發展和優劣,這裏不許備作過多的討論。我想說的是,在模塊化以後還有一個模塊間耦合的問題,若是模塊間耦合度大也會下降代碼的可重用性或者說複用性。因此也出現了下降耦合的觀察者模式或者發佈/訂閱模式。這對於提高代碼重用,複用性和避免單點故障等都很重要。說到這裏,還想順便提一下最近流行起來的響應式編程(RxJS),響應式編程中有一個很核心的概念就是 observable,也就是 Rx 中的流(stream)。它能夠被 subscribe,其實也就是觀察者設計模式。

補充閱讀

總結

將來前端複雜度不斷增長已成定論,隨着後端成熟,天然會將焦點轉移到前端領域,並且服務化、用戶體驗愈來愈重要,前端體驗早不是當初能看就行,任何網頁的異常、視覺的差別,或文案的模糊,都會致使用戶流失,支付中斷。前端對公司營收的影響,漸漸與後端服務宕機同等嚴重,因此前端會愈來愈重,異常監控,性能檢測,工具鏈,可視化等等都是這幾年你們逐漸重視起來的。

咱們早已不能將 javascript 早期玩具性質的模塊化方案用於現代愈來愈重要的系統中,前端界必然出現同等重量級的模塊化管理方案,感謝 TC39 制定的 ES2015 模塊化規範,咱們已經離不開它,哪怕全部人必須使用 babel。

話說回來,標準推動的太慢,咱們仍是把編譯工具看成常態,抱着哪怕支持了 ES2015 全部特性,babel 依然還有用的心態,將預編譯進行到底。一句話,模塊化仍在路上。js 模塊化的矛頭已經對準了 css 與 html,這兩位元老也該向前衛的 js 學習學習了。

將來 css、html 的模塊化會自立門戶,仍是賦予 js 更強的能力,讓二者的模塊化依附於 js 的能力呢?目前 html 有自立門戶的苗頭(htmlModules),而 css 遲遲沒有改變,社區出現的 styled-component 已經用 js 將 css 模塊化得很好了,最新 css 規範也支持了與 js 的變量通訊,難道但願依附於 js 嗎?這裏但願獲得你們更普遍的討論。

我也認同,畢竟壓縮、混淆、md五、或者利用 nonce 屬性對 script 標籤加密,都離不開本地構建工具。

聽說 http2 的優化中,有個最佳文件大小與數量的比例,那麼仍是脫離不了構建工具,前端將來會愈來愈複雜,同時也愈來愈美好。

至此,對於 javascript 模塊化討論已接近尾聲,對其優缺點也基本達成了一致。前端複雜度不斷提升,促使着模塊化的改進,代理(瀏覽器、node) 的支持程度,與前端特殊性(流量、緩存)可能前端永遠也離不開構建工具,新的標準會讓這些工做作的更好,同時取代、加強部分特徵,前端的將來是更加美好的,複雜度也更高。

若是你想參與討論,請點擊這裏,每週都有新的主題,每週五發布。

相關文章
相關標籤/搜索