最近看了不少關於用webpack做程式碼切割和持久快取的文章,又因為這個被公司的大神虐了很久。 寫一點收貨,也不枉被虐了一番。
npm/yarn
這種方法網上關於webpack的文章裡很常見,就是通過npm/yarn進行安裝,然後由webpack直接打成模組放進引用他的chunk裡。
當然,庫的程式碼和業務程式碼混在一起肯定是不行的,webpack提供了CommonsChunkPlugin,dllPlugin等外掛(webpack4似乎幹掉了CommonsChunkPlugin,取而代之的是splitChunks和。。。)來做程式碼切割。這裡不再贅述具體方法,另外有寫一篇小結做詳細介紹。
我理解最基本的的結果大概是:
- vendor,業務中要用到的第三方庫庫,可以獨立提取出來打到一個chunk。
- 在非同步chunk中使用到的庫可以提取到一個async-vendor中。
- 業務程式碼單獨打到一個chunk(當然,如果有必要,業務程式碼也可以再做一個公共程式碼的提取)
- webpack runtime程式碼。
總之呢,就是第三方庫都被統一塞到了一個或幾個檔案中,組成了體積比較大的vendor chunk。
這麼做的好處是什麼呢?
- 所有的vendor通過一個或幾個http請求就搞定了。
- webpack幫我們省了很多工作,比如如何按照正確的順序線性引用第三方庫。
- 伺服器做gzip的時候,對大尺寸的檔案做壓縮效果會比小檔案好(因為更容易出現重複的字串等)。
- 可能和第一點有點矛盾,如果可以使用http2,那麼http通訊的消耗大大減小,那麼我們應該儘可能的把程式碼拆散。為此,webpack提供了AggressiveSplittingPlugin將大chunk拆成若干個小尺寸的chunk,粒度可以比externals按庫切分還細的多。
壞處呢?
- 如果不用AggressiveSplittingPlugin,所有庫只打一個或者幾個vendor,粒度還是太粗了,用一個庫就得載入一整個vendor,也不好做按需載入。 如果用AggressiveSplittingPlugin,又需要相容不支援http2的瀏覽器的話,表現可能就比較糟糕了。
- 每一次build,即使內容沒有變化,這些vendor chunk都會被webpack重新生成一遍,浪費時間。
externals
如果不想通過npm來安裝第三方庫,webpack很貼心的提供了externals介面來讓使用者自己引入需要的庫。
作法呢,很簡單。把業務中用到的第三方庫通通external掉,然後再通過script把庫程式碼通過global引入。
externals的好處:
- 每一個庫就是一個chunk,不常用的庫可以拿出來做按需載入。一個庫的變動不會影響其他庫的快取命中率。
- 因為是手動引入的,webpack每次build都可以跳過這些庫,節省不少時間。
- 和上面相呼應的,如果可以使用http2,拆的粒度理論上是越細越好。每個庫都生成一個chunk顯然要比打到一個vendor要好。
壞處:
- 因為是手動引入,所以如果庫之前存在依賴關係,自己得管理好載入順序。
總的來說呢,兩種方法肯定都是可以實現持久化快取的,但是從切分粒度上來說,用npm/yarn可以切的很粗糙,也可以很細緻。而externals就是一個庫一個chunk(我認為這樣比較適中)。當然,具體哪個好要看自己的業務需求。 另外一個角度來看,build整個工程相較於庫的變更肯定是一個相當高頻的操作,更值得優化。