【譯】webpack 小札: 充分利用 CommonsChunkPlugin()
- 原文地址:webpack bits: Getting the most out of the CommonsChunkPlugin()
- 作者:Sean T. Larkin
- 翻譯人:李紹懿
webpack 核心團隊喜歡不時地參與到Twitter社區中,通過一些有趣並且有營養的方式分享知識。


我們發現了什麼?
最常見的問題就是代碼重複:重複的類庫、組件、代碼存在於多個(同步或非同步)的bundle里!
例子1:很多帶有重複代碼的 vendor bundle


Swizec Teller人很好,分享了他的一個構建(實際上是包含了8-9個獨立單頁的應用)。我從中挑選了這個例子因為可以從中辨認出很多很棒的技術。讓我們具體來看看:

除了「FoamTree」圖表之外都是應用的代碼,與此同時,所有用了node_modules資源的部分都以"_vendor.js" 結尾
我們可以從中推斷出不少東西(在不看具體配置的情況下)。
每個單頁應用都僅僅在入口和vendor代碼中用了 CommonsChunkPlugin 插件。這創建了一個只包含來自node_modules文件代碼的bundle,並且其他的bundle只包含了應用代碼。以下是部分相關配置:

Object.keys(activeApps)n .map(app => new webpack.optimize.CommonsChunkPlugin({n name: `${app}_vendor`,n chunks: [app],n minChunks: isVendorn }))n
activeApps 變數代表了每個獨立的入口點。
機會區域
下面是我圈出來可以做一些改善的區域。

「元」 緩存
我們上面看到的是各種諸如 momentjs, lodash 和 jquery 的龐大類庫穿插在6個甚至更多的vendor bundle中使用。把所有vendor打包到一個單獨bundle的策略固然是好的,但我們也應該相同的策略應用到所有的 vendor bundle內部。
new webpack.optimize.CommonsChunkPlugin({n children: true,n minChunks: 6n})n
這相當於我們告訴webpack:
嘿,webpack, 看看所有的 chunk (包括已生成的vendor),然後把出現在6個以上chunk中的模塊放到一個單獨的文件中。



正如你所見,所有的特定模塊被抽離到一個獨立的文件,並且據 Swizec說,這讓他們的應用大小整體下降了17%!
例子2:非同步 chunk 之間的重複 vendor

這是個令人印象深刻的代碼拆分用法。瞧瞧這些漂亮的顏色
所以這個代碼的重複量相對整體代碼不那麼嚴重吧,然而,當你看到下面全量代碼體積的圖時,你會發現有三個同樣的模塊存在於每個非同步chunk中。

正如你在上面看見的,有2,3個相同的組件用到了四五十個非同步bundle中。那麼我們怎麼通過 CommonsChunkPlugin 解決這個問題呢?
創建非同步的 Commons Chunk
這個技術和第一個非常相似,我們要把配置文件中的 async 屬性設為 true ,像這樣:
new webpack.optimize.CommonsChunkPlugin({n async: true,n children: true,n filename: "commonlazy.js"n});n
用同樣的方式 - webpack 會掃碼所有的 chunk 來查找通用模塊。當async為true時,只有拆分代碼的bundle才會被掃描。 由於我們並沒有指定minChunks,所以默認值是3。webpack相當於被告知:
嘿,webpack, 查看所有的常規(又稱懶載入)chunk,如果你找到了出現在3個以上chunk中的相同模塊,那麼請把它抽離到一個單獨的非同步通用chunk中。
最終的結果就是:


可能有更大的minChunks值,來產生一個更小的commonlazy.js包
現在的非同步chunk已經非常小了,並且所有的代碼都聚合到了一個叫 commonlazy.js 的文件中。由於這些bundle已經很小了,所以大小的影響直至第二次訪問都不是很明顯。 現在,被分隔到每個bundle中的代碼少了很多,我們通過將這些通用模塊放入單獨的可高速緩存的chunk中來節省用戶載入時間和數據的消耗。
更進一步控制: minChunks 函數

new webpack.optimize.CommonsChunkPlugin({n filename: "lodash-moment-shared-bundle.js",n minChunks: function(module, count) {n return module.resource && /lodash|moment/.test(module.resource) && count >= 3n }n})n
上面的例子就是說:
喲,webpack, 當你掃描模塊時候,如果模塊的絕對路徑匹配到了lodash或者momentjs並且出現在了三個入口文件中,那麼把這些模塊打包到一個bundle中。
你可以通過設置「async:true」將同樣的行為應用於非同步bundle。
再多的控制

function lodashMomentModuleFilter(module, count) {n return module.resource && /lodash|moment/.test(module.resource) && count >= 2;n}nfunction immutableReactModuleFilter(module, count) {n return module.resource && /immutable|react/.test(module.resource) && count >=4n}nnew webpack.optimize.CommonsChunkPlugin({n filename: "lodash-moment-shared-bundle.js",n minChunks: lodashMomentModuleFiltern})nnew webpack.optimize.CommonsChunkPlugin({n filename: "immutable-react-shared-bundle.js",n minChunks: immutableReactModuleFiltern})n
沒有銀彈!
CommonsChunkPlugin() 或許很強大,但請記住,這些例子都有它們所適用的場景。所以當你複製粘貼這些代碼片段之前,從Sam Saccone、Paul Irish還有MPDIA那先獲得一些建議,以確保你使用的是正確方案。

從哪找到更多的例子?
這些只是使用和配置 CommonsChunkPlugin() 的例子。要想了解更多,在我們的 webpack/webpack GitHub 核心倉庫中查看 /examples 目錄!如果你還有更好的想法,歡迎提交PR!
推薦閱讀:
※webpack:從入門到真實項目配置
※簡單幾步助你優化React應用包體
※讀懂webpack生成的26行代碼
※webpack 打包JS 的運行原理
※淺析 Webpack 插件化設計
