http://refined-x.com/2017/06/16/Webpack%E6%98%AF%E7%AD%94%E6%A1%88%E5%90%97/
webpack剛出現時gulp如日中天,現在webpack更新到2.x版本,gulp逐漸淡出我們的視線,聊webpack的人越來越多,直到最近發現Vue官方文檔里到處都是基於webpack的講解,仿佛webpack已經成為了打包器的事實標准,作為一個仍然不准備使用webpack的前端,我必須認真打量一下自己的處境了。
背景知識
模塊化開發
一切還要從模塊化說起,在距今僅僅幾年之前的前端頁面里,還會在底部發現一個jQuery+一個js文件的鏈接,隨着這幾年規范的進步、硬件、網絡的發展、人們對體驗的重視,各種因素導致前端腳本越寫越多,代碼越來越復雜,仿佛一夜之間那個玩具般的代碼組織方式無法滿足前端開發需求了,於是js模塊化橫空出世,以橫掃之勢迅速被前端社區接受,依托加載器實現的js模塊化開發和加載,大大提升了前端開發體驗,seajs便是在那個背景下殺出來的優秀加載器之一。
模塊打包
模塊化開發固然好,但在加載上有點問題,因為前端性能優化向來很看重減少請求數量,模塊化加載無疑是背道而馳,要解決這個問題,大家想到了將模塊打包,開發時是分開的,發布前用打包器合到一起,這樣頁面的js請求數可以降到1,很好,從此打包器開始流行,webpack就是一個打包器,在webpack之前大家用的最多的是gulp,gulp是一個比打包器更底層的任務管理器,可以壓縮代碼,預編譯代碼,處理圖片,當然也可以完成打包。
webpack的反殺
既然gulp被webpack反殺了,一定是有不如人的地方,以gulp為代表的傳統打包最大的問題是解決不了按需打包,就更別說按需加載了,因為傳統的打包思路是遍歷源文件 => 匹配規則 => 打包/處理,也就是說只要被規則命中了,即便是程序用不到的模塊也會被無腦打包,根本原因是按需這個事無法被規則描述,只能被程序邏輯描述。其實在webpack之前不是沒有解決方案,百度fis最得意的地方就是解決了這個問題,並且是理論上堪稱完美的解決方案,我感覺其最大的缺點是需要后端配合,然而你懂的,后端通常不鳥這種需求,百度fis也就不了了之了。
那webpack是怎么解決按需問題的呢?前面說了,按需只能被程序邏輯描述,webpack的打包思路就是從程序邏輯入手:入口文件 => 分析代碼 => 找出依賴 => 打包,這樣代碼里不出現的模塊就不可能被打進包里,甚至還可以實現按需加載,這就是webpack最有價值的地方。
打包過程中還有一個僅次於按需的需求,那就是分包,之所以說webpack或者說所有的打包器都特別適合SPA應用,是因為SPA只有一個入口,基本不需要分包或者分包需求很簡單,但在網站類應用上,我們討論的不是要不要分包,而是怎樣最不浪費的分包,將加載總量降到最低。這個通常來說不是大問題,只要配合好文件目錄結構的划分,多數分包需求都可以用規則描述,但在這方面webpack更進了一步,可以在業務代碼中依托特定的語法標記出需要分包的模塊,這就使分包的實現能在一定程度上自動化了,沒毛病。
webpack的其他功能我覺得沒必要再說了,都不是剛需,有則錦上添花,沒有也無所謂。
需求分析
業務場景
不談場景的選型都是耍流氓。
假設有這樣一個網站項目,兼容IE8,基於jQuery開發,腳本模塊化,模塊分成業務模塊、插件模塊、類庫模塊,類庫模塊以jQuery為主,業務模塊即每個頁面的業務代碼,也是腳本入口,插件模塊主要實現各種前端展示效果,為了更好的代碼復用,已經將很多插件封裝好並沉淀為一個插件庫,可以覆蓋項目里幾乎所有的展示需求,這個庫以一個文件夾的形式存在。
那么問題來了,這么多模塊,請求數居高不下,怎么辦。
無腦回答,打包啊。
打包分析
那我們就來看這個項目該怎么打包,重點放在插件模塊上,首先會遇到傳統打包的老大難按需問題,因為插件都在一個文件夾里,規則只能全部匹配,結果就是打出來一個大而無當的包。你說手動把不用的插件移除不行么,行。但實際情況是,展示類頁面經常更新,可能今天用到插件1,明天就改用插件2,或者增加了一個頁面要用插件3,你讓開發者自己去統計到底用了哪個沒用哪個嗎,那是歷史的倒退,不行。
別人解決不了webpack可以,這是webpack首當其沖的優勢,按需,不是問題。
再往下看,網站上的所有展示效果都通過插件實現,最終整個項目用到的插件總數比較可觀,也就是說即便按需了,這個插件包也不會小,而插件雖說相對固化,但畢竟是展示類插件,更新是免不了的,任意一個插件發生了增刪改,整個插件包都要重打,客戶端重新加載,代價有點大,不划算,並且我們知道,這個需求對於展示型網站來說,絕對是個真需求。
整體打包不行,那每個頁面對應一個自己的插件包呢,各改各的,互不干擾,嗯,有點道理。
但是,實際情況要復雜的多,這也是展示型網站最煩人的地方,頁面間還是有不少共用插件的,頭部至少有個導航、下拉菜單、搜索框吧,底部來個選項卡不過分吧,幻燈片插件很多頁面都有可以嗎,這些公共插件每個頁面打一份真的好嗎。要是將公用插件單獨拎出來呢,好啊,那不就又是人肉維護依賴關系了么,還是不好。
說一千道一萬,關鍵在於打包需求不穩定,這種情況是打包中最難解決的,webpack也沒有辦法。但百度fis有!fis從一個更高的維度看問題,直接拿到了資源依賴表,千變萬化的打包需求瞬間變得可控了,可惜,用戶少,生態就小,生態小,就沒戲了。
現在的局面,在不考慮fis的情況下,已經不是怎么打包的問題了,而是根本不適合打包。
解決方案
模塊本地存儲
那么問題又變成,無法打包的情況下如何減少模塊化應用請求數。
不打包就得用加載器,加載器自身不可避免的要占一個請求,但加載器也為模塊請求優化提供了可能,現在至少有兩種可行的方案,一是配合服務端的請求合並,二是模塊本地存儲。服務端請求合並方案這里略去不談。
本地存儲方案在純前端就可以實現,通過擴展加載器將請求到的模塊代碼緩存到localStorage,這樣用戶初次訪問會全量請求,但之后的訪問將只加載模塊加載器、緩存模塊目錄、和業務模塊三個文件,模塊目錄文件用於記錄將要緩存的模塊及其版本號,實現緩存更新,實際開發中可以跟加載器合並成一個文件,從而將腳本請求數降到2。
下面我們就要算一筆賬了,雖然打包方案極難完美實現,但如果我們財大氣粗,上CDN加速呢,雖然包經常更新,但有了CDN加載速度也能保證,理論上每個頁面的腳本請求數可以降到1,但實際中總要提取第三方類庫吧,請求數同樣是2,好,那對比雙方就是:
普通請求加載器 + 業務代碼 VS CDN加持下的公共包 + 頁面包:
下面討論的前提是,CDN再快也有極限,一個CDN大包在一個小文件面前,不會有很大優勢。下面看看雙方的代碼體積,加載器用的seajs,頁面的業務代碼百行左右,這種情況下的2個請求,我認為上CDN的錢可以省。在非首次加載情況下,我認為本地存儲方案處於無敵水平。
本地存儲方案最大的問題是首次加載,大批的插件請求怎么辦。無法根除,但可以緩解。比如在前端開發時多考慮模塊懶加載,盡量提升首屏展示速度,這是前端的看家本領。實在不行咱也可以給插件上CDN嘛,反正只有首次加載走流量,就算上了CDN都比其他方案省好多錢。
因此最終選定的方案是,seajs + 本地存儲。
webpack不是銀彈
以上項目需求均來自真實經歷,這些需求我相信在前端圈子里仍然占有很大的比例,總結起來就是,我們有一個豐富又多變的組件庫,這時候webpack解決不了問題。
在兼容性方面,webpack一直是面向最新的標准,自身的很多特性需要依賴polyfill才能向下兼容,甚至有的特性最新的瀏覽器都還沒有原生兼容,用起來難免有點折騰。
技術圈都是追新的,仿佛現在的前端都不好意思再聊jQuery技術棧了,即便說起來也都是jQuery已經過時了的論調,我只想說,瀏覽器市占率擺在那,展示類項目的需求擺在那,這些應用場景還確確實實的存在着,現實世界的任何一個2C產品可能都有一個高到讓你不屑的兼容性要求。
最后webpack的侵入性較強,這是我個人最介意的一點,某些高級特性需要依賴獨特的語法才能實現,也就是說需要在一定程度上面向webpack編程,一旦離開了webpack,你的源碼無法運行。
后記
唯一不變的是變化。
模塊加載在未來大概率會被瀏覽器原生支持,到那時會不會有新的優化方案?
打包這件事,怎么看都像一個補丁,未來的HTTP2會不會徹底使其成為偽需求?
IE8遲早淘汰,Vue/React的市場會無限增大嗎,SEO怎么做,Nodejs服務端渲染是答案嗎?
這真是個動盪的年代啊。
沒錯,文中提到的項目就是Flow-UI,感謝關注。
