【譯】webpack 小札: 充分利用 CommonsChunkPlugin()

  • 原文地址:webpack bits: Getting the most out of the CommonsChunkPlugin()
  • 作者:Sean T. Larkin
  • 翻譯人:李紹懿

webpack 核心團隊喜歡不時地參與到Twitter社區中,通過一些有趣並且有營養的方式分享知識。

這一次,「遊戲規則」很簡單。安裝 webpack-bundle-analyzer ,生成一張精美多彩的圖片,包含了你所有的bundle,然後分享給我。作為回報,webpack團隊會幫你指出一些我們能辨認的潛在問題。

我們發現了什麼?

最常見的問題就是代碼重複:重複的類庫、組件、代碼存在於多個(同步或非同步)的bundle里!

例子1:很多帶有重複代碼的 vendor bundle

這是一個典型例子。Swizec謝謝你讓我分享它。

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中的模塊放到一個單獨的文件中。

在這個例子中這個文件似乎被命名為「manifest.js」

正如你所見,所有的特定模塊被抽離到一個獨立的文件,並且據 Swizec說,這讓他們的應用大小整體下降了17%!

例子2:非同步 chunk 之間的重複 vendor

這是個令人印象深刻的代碼拆分用法。瞧瞧這些漂亮的顏色

所以這個代碼的重複量相對整體代碼不那麼嚴重吧,然而,當你看到下面全量代碼體積的圖時,你會發現有三個同樣的模塊存在於每個非同步chunk中。

非同步chunk就是文件名為「[number].[number].js」的文件

正如你在上面看見的,有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 函數

如果你想要更多控制會怎麼樣?有時候你並不想要單個共享的bundle,因為並不是每個 懶/入口 chunk 都會用到它。 minChunks 屬性也支持 function !!這可以讓你篩選哪些模塊會被加入你新的bundle中。舉個例子:

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。

再多的控制

通過這個 minChunks 你可以為特定的入口和bundle創建更小的可緩存vendor集合。最後,你看到的可能會是這樣:

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 插件化設計

TAG:webpack | 前端开发工具 |