webpack 拾翠:充分利用 CommonsChunkPlugin()

webpack 核心團隊隔三差五地就會在 Twitter 上做一些寓教於樂的技術分享javascript

Markdown

此次的「遊戲規則」很簡單:安裝 webpack-bundle-analyzer,生成一張包含全部 bundles 信息的酷炫圖片分享給我,而後 webpack 團隊會幫忙指出任何潛在的問題。前端

咱們發現了什麼?

最多見的問題是代碼重複:庫、組件、代碼在多個(同步的、異步的)bundles 中重複出現。java

案例一:不少重複代碼的 vendor bundles

Markdown

Swizec Teller 分享了一個構建圖(其實是對 8-9 個獨立單頁應用的構建)。在衆多例子中我決定選擇這一個,由於咱們能夠從中學到不少技術,下面讓咱們來仔細分析一下:node

距離 「FoamTree」 圖標最近的是應用自己的代碼,而其餘全部 node_modules 的代碼則是左邊那些以 "_vendor.js" 結尾的。react

單從這幅圖(不須要看實際配置文件)中咱們就能推斷出不少事情。jquery

每一個單頁應用都運用了一個 new CommonsChunkPlugin ,並以其 entry 和 vendor 代碼爲目標。這會生成兩個 bundles,一個只包含 node_modules 裏面的代碼,另外一個則只包含應用自己的代碼。(Swizec Teller)甚至還提供了部分配置信息:android

Markdown

Object.keys(activeApps)
  .map(app => new webpack.optimize.CommonsChunkPlugin({
    name: `${app}_vendor`,
    chunks: [app],
    minChunks: isVendor
  }))複製代碼

其中 activeApps 變量極可能是用來表示獨立入口點的。webpack

能夠優化的地方

下面幾個畫圈的是能夠優化的地方。ios

「Meta」 緩存

從上圖能夠看出,許多大型代碼庫(例如 momentjs、lodash、jquery 等)同時被 6 個(甚至更多) bundles 用到了。將全部 vendors 打包到一個獨立 bundle 中的策略是很好的,但其實對全部 vendor bundles 也應該採起一樣的策略。git

我建議 Swizec 將以下插件添加到插件數組的末尾

new webpack.optimize.CommonsChunkPlugin({
  children: true, 
  minChunks: 6
})複製代碼

這是在告訴 webpack:

嘿 webpack,請檢查全部的 chunks(包括那些由 webpack 生成的 vendor chunks),找出那些在 6個及6個以上 chunks 中都出現過的模塊,並將其移到一個獨立的文件中。

Markdown

Markdown

如你所見,如今全部符合要求的模塊都被抽離到一個獨立的文件中,Swizec 指出這個應用程序大小下降了 17%。

案例二:異步 chunks 中的重複 vendors

Markdown

就總體代碼體積來講,這種數量的重複並不嚴重;可是,若是你看到下面這張完整大圖,你就會發現每個異步 chunk 中都有 3 個如出一轍的模塊。

異步 chunks 是指那些文件名中包含 "[number].[number].js" 的 chunk。

如上圖所示,四五十個異步 bundles 都用到了兩三個一樣的組件,咱們該如何利用 CommonsChunkPlugin 來解決此問題呢?

建立一個異步 Commons Chunk

解決方法和第一個案例中的相似,可是須要將配置選項中的 async 屬性設爲 true,代碼以下:

new webpack.optimize.CommonsChunkPlugin({
  async: true, 
  children: true, 
  filename: "commonlazy.js"
});複製代碼

相似地 —— webpack 會掃描全部 chunks 並檢查公共模塊。因爲設置了 async: true,只有代碼拆分的 bundles 會被掃描。由於咱們並無指明 minChunks 的值,因此 webpack 會取其默認值 3。綜上,上述代碼的含義是:

嘿 webpack,請檢查全部的普通(即懶加載的)chunks,若是某個模塊出如今了 3 個或 3 個以上的 chunks 中,就將其分離到一個獨立的異步公共 chunk 中去。

效果以下圖所示:

Markdown

如今異步 chunks 都很是的小,而且全部代碼都被聚合到 commonlazy.js 文件中去了。由於這些 bundles 原本就很小了, 首次訪問可能都察覺不到代碼體積的變化。如今,每個代碼拆分的 bundle 所需攜帶的數據更少了;並且,經過將這些公共模塊放到一個獨立可緩存的 chunk 中,咱們節省了用戶加載時間,減小了須要傳輸的數據量(data consumption)。

更多控制:minChunks 函數

Markdown

那若是你想要跟多的控制權呢?某些狀況下你可能並不想要一個單獨的共享 bundle,由於並非每個懶加載/入口 chunk 都要用到它。minChunks 屬性的取值也能夠是一個函數!該函數能夠用做「過濾器」,決定將哪些模塊加到新建立的 bundle 中去。示例以下:

new webpack.optimize.CommonsChunkPlugin({
  filename: "lodash-moment-shared-bundle.js", 
  minChunks: function(module, count) { 
    return module.resource && /lodash|moment/.test(module.resource) && count >= 3
  }
})複製代碼

上例含義是:

呦 webpack,若是你發現某個模塊的絕對路徑和 lodash 或 momentjs 相匹配而且出如今了 3 個(或 3 個以上)獨立的 entries/chunks 中,請將其抽取到一個獨立的 bundle 中去。

經過設置 async: true,你也能夠將此方法應用到異步 bundles 中。

更多更多控制

Markdown

有了這種 minChunks,你就能夠爲特定的 entries 和 bundles 生成更小的可緩存 vendors 的子集。最終,你的代碼看起來大概就像這樣:

function lodashMomentModuleFilter(module, count) {
  return module.resource && /lodash|moment/.test(module.resource) && count >= 2;
}

function immutableReactModuleFilter(module, count) {
  return module.resource && /immutable|react/.test(module.resource) && count >=4
}

new webpack.optimize.CommonsChunkPlugin({
  filename: "lodash-moment-shared-bundle.js", 
  minChunks: lodashMomentModuleFilter
})

new webpack.optimize.CommonsChunkPlugin({
  filename: "immutable-react-shared-bundle.js", 
  minChunks: immutableReactModuleFilter
})複製代碼

沒有銀彈!

CommonsChunkPlugin() 當然很強大,但要記住本文中的例子都是針對特定應用的。所以,在複製-粘貼這些代碼片斷以前,請先聽聽 Sam SacconePaul IrishMPDIA 的建議,避免用錯了方法。

在應用解決方法以前,必定要理解方法背後的思路!

哪裏還有更多例子?

上述只是 CommonsChunkPlugin() 的部分用例,更多資源請參考咱們 webpack/webpack core GitHub 倉庫中的 [/examples](https://github.com/webpack/webpack/tree/master/examples) 目錄。若是你還有其餘好想法,歡迎 Pull Request

沒時間貢獻代碼?但願以其餘方式作貢獻?向咱們的 open collective 捐款,即刻成爲贊助商。Open Collective 不只爲核心團隊提供支持,同時也幫助那些爲提高咱們社區質量而花費了大量寶貴的空閒時間的貢獻者們!❤


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索