【譯】SplitChunksPlugin [webpack]

(免責聲明:本譯文未參考任何其他文章,如有與其他譯版雷同部分,純屬巧合)
(聲明:中文版權歸作者zbc所有,轉載請注明出處)
翻譯原文地址(webpack官網)


原先,被打包的代碼塊(和被引入的組件塊)都是以一種類似“父—子”模式的關系在 webpack 內部依賴圖中關聯(lián)著锋拖。CommonsChunkPlugin 就是用來避免依賴塊被重復引用,但是沒有其他優(yōu)化功能祸轮。

自從 webpack4 以來兽埃,CommonsChunkPlugin 已經被移除,取而代之的是 optimization.splitChunks适袜。

默認

SplitChunksPlugin 適用于絕大多數用戶柄错。
默認情況下,它只影響按需(on-demand)加載的代碼塊苦酱,因為改變初始代碼塊會影響運行項目時 HTML 文件包含的 <script /> 標簽售貌。

webpack 會基于如下原則自動分割代碼:

  • 公用代碼塊或來自 node_modules 文件夾的組件模塊。
  • 打包的代碼塊大小超過 30k(最小化壓縮之前)疫萤。
  • 按需加載代碼塊時颂跨,同時發(fā)送的請求最大數量不應該超過 5。
  • 頁面初始化時扯饶,同時發(fā)送的請求最大數量不應該超過 3恒削。

當試圖完成后兩項時池颈,總會生成較大體積的代碼塊。

配置

webpack 提供一系列配置項給想要更多功能的開發(fā)者钓丰。

默認配置已被選擇為 web 表現(xiàn)層最佳方案躯砰,但是這與你項目的最佳策略可能有所不同。當你改變配置項時携丁,你應當測試這些修改確實是為你帶來了收益琢歇。

optimization.splitChunks

下面這個配置對象體現(xiàn)為 SplitChunksPlugin 的默認配置狀態(tài)。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

splitChunks.automaticNameDelimiter

< string >

webpack 默認會使用原代碼塊名稱來命名新生成的代碼塊(例如:vendor~main.js)梦鉴。該配置項可以讓你來自定義代碼塊名稱分隔符李茫。

splitChunks.chunks

< function(chunk) | string >

該項指定了哪些塊將被優(yōu)化。如果配置為字符串的話尚揣,可選值為“all”, “async”, “initial”涌矢。選擇 “all” 的功能最為強大,因為這將意味著公共代碼塊可以同時被異步和非異步塊調用快骗。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      // include all types of chunks
      chunks: 'all'
    }
  }
};

或者你也可以選擇傳入一個函數來控制。其返回值將決定是否應該包含每一個模塊塔次。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks (chunk) {
        // exclude `my-excluded-chunk`
        return chunk.name !== 'my-excluded-chunk';
      }
    }
  }
};

你可以結合 HtmlWebpackPlugin 使用此配置項方篮,它將為你添加所有新生成的代碼塊。

splitChunks.maxAsyncRequests

< number >

按需加載模塊時所允許的最大請求數励负。

splitChunks.maxInitialRequests

< number >

入口點所允許的最大請求數藕溅。

splitChunks.minChunks

< number >

代碼拆分前的最小公共塊數量。

splitChunks.minSize

< number >

以比特計算继榆,生成代碼塊的最小體積巾表。

splitChunks.maxSize

< number >

使用 maxSize(無論是全局值 optimization.splitChunks.maxSize,還是每一個緩存組值 optimization.splitChunks.cacheGroups[x].maxSize略吨,還是回調緩存組值 optimization.splitChunks.fallbackCacheGroup.maxSize)會通知 webpack 嘗試分割體積大于設定的 maxSize 值的代碼塊集币。分割出的塊大小將至少為 minSize 值(最大至 maxSize 值)。算法是確定的翠忠,并且當模塊改變時只會影響本地鞠苟。所以對于使用長期緩存且不需要記錄的需求很有用處。maxSize 只是一個提示秽之,并且當模塊大于 maxSize 或分割至小于 minSize 時也完全可以違反之当娱。
一旦一個塊已經有了名稱,每個其他部分都會以之為衍生獲得一個名稱考榨,這個名稱取決于 optimization.splitChunks.hidePathInfo 的值跨细,并且添加一個從起始塊派生來的鍵名或哈希值。
maxSize 是打算用于配合 HTTP/2 和長效緩存使用的河质。它增加了請求數以求更好的緩存冀惭,它也可以用于快速重構即減小文件體積震叙。

maxSize 的優(yōu)先級高于 maxInitialRequest/maxAsyncRequests,事實上優(yōu)先級關系為如下:
maxInitialRequest/maxAsyncRequests < maxSize < minSize.

splitChunks.name

< boolean: true | function (module, chunks, cacheGroupKey) | string >

分割塊的名字云头。如果傳入 true 將會自動生成一個基于塊組和緩存組鍵的名稱捐友。

傳入一個字符串或函數將允許你自定義塊名稱。指定一個字符串或是一個始終返回同一字符串的函數都會使得 webpack 將所有公共模塊融合進一個唯一大代碼塊溃槐。這可能導致一個巨大的初始化下載過程匣砖,使得頁面加載緩慢。
如果splitChunks.name與入口點名稱匹配昏滴,則該入口點將被刪除猴鲫。
推薦將splitChunks.name設置為 false ,以避免在打包產品的時候進行不必要的改名動作谣殊。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      name (module, chunks, cacheGroupKey) {
        // generate a chunk name...
        return; //...
      }
    }
  }
};

當為不同的拆分塊分配相同的名稱時拂共,所有打包模塊都被放置在一個共享塊中,因為這樣會導致更多的代碼下載姻几,所以不建議這樣做宜狐。

splitChunks.cacheGroups

cacheGroups 可以繼承或覆蓋任何來自 splitChunks.* 的設定;但是 "test", "priority","reuseExistingChunk" 只能在本層級上設定蛇捌。為避免使用任何 cacheGroups 默認設置抚恒,把它們統(tǒng)統(tǒng)設置為 false。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false
      }
    }
  }
};

splitChunks.priority

< number >

一個模塊可以屬于多個緩存組络拌。優(yōu)化過程總是偏愛優(yōu)先級更高的緩存組俭驮。默認組的優(yōu)先級為負,以便為自定義組提供相對較高的優(yōu)先級(自定義組的優(yōu)先級默認為0)春贸。

splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk

< boolean >

如果當前塊包含一個已經從主打包文件中分離出來的模塊混萝,那么它將被重復使用而不是新生成模塊。這將影響最終打包生成的塊文件名稱萍恕。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          reuseExistingChunk: true
        }
      }
    }
  }
};

splitChunks.cacheGroups.{cacheGroup}.test

< function (module, chunk) | RegExp | string >

控制那些被緩存組選中的模塊逸嘀。省略它將選擇所有模塊。它可以匹配模塊資源絕對路徑或塊名稱雄坪。一旦一個塊被匹配成功厘熟,當中的所有模塊也一同被選中。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          test(module, chunks) {
            //...
            return module.type === 'javascript/auto';
          }
        }
      }
    }
  }
};

splitChunks.cacheGroups.{cacheGroup}.filename

< string >

當且僅當一個塊是初始塊時维哈,允許覆蓋文件名绳姨。可以獲得output.filename的所有字符阔挠。

此配置項也可以在全局的 splitChunks.filename 里面設置飘庄,但是不推薦這么做,因為當 splitChunks.chunks 沒有被設置為 "initial" 時购撼,可能會導致錯誤跪削,應避免如此做法谴仙。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          filename: '[name].bundle.js'
        }
      }
    }
  }
};

splitChunks.cacheGroups.{cacheGroup}.enforce

< boolean: false >

告知 webpack 應該忽略
splitChunks.minSize,splitChunks.minChunks,splitChunks.maxAsyncRequests
splitChunks.maxInitialRequests 的配置項,并且總是為此緩存組創(chuàng)建塊碾盐。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          enforce: true
        }
      }
    }
  }
};

舉例

默認:例1

// index.js

import('./a'); // 動態(tài)引入
// a.js
import 'react';

//...

結果:創(chuàng)建了一個包含 react 的獨立塊晃跺。再導入調用的時候,此塊與包含“./a” 的原始塊并行加載毫玖。
為什么:

  • 條件1:此塊包含來自 “node_modules” 文件夾的模塊掀虎。
  • 條件2:react 包大于30k。
  • 條件3:并行的請求調用數為2付枫。
  • 條件4:不影響頁面初始加載時的需要烹玉。

這之后可以推論出什么?react 可能不會像你的應用代碼那樣頻繁變動阐滩。通過將它轉移到一個單獨的文件包中可以與你的應用代碼分別儲存(假設你正使用塊哈希二打,記錄,緩存控制或其他長效緩存手段)掂榔。

默認:例2

// entry.js

// dynamic imports
import('./a');
import('./b');
// a.js
import './helpers'; // helpers is 40kb in size

//...
// b.js
import './helpers';
import './more-helpers'; // more-helpers is also 40kb in size

//...

結果:創(chuàng)建了一個包含 "helpers.js" 及其所有依賴的單獨塊继效。導入調用此塊時,與原始塊并行加載装获。
為什么:

  • 條件1:此塊被兩處調用莲趣。
  • 條件2:helpers.js 文件大于30k。
  • 條件3:并行的請求調用數為2饱溢。
  • 條件4:不影響頁面初始加載時的需要。

將 helpers 代碼分發(fā)至每一個模塊會導致此部分代碼被下載兩次走芋。將之分離出來就可以是這一下載過程只發(fā)生一次了绩郎。我們付出一次額外請求的代價以折衷此間消耗,這也是為什么我們把分離塊的最小判斷值設置為30k的原因翁逞。

分離塊:例1

創(chuàng)建一個包含所有入口點調用的公共代碼 common 塊肋杖。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2
        }
      }
    }
  }
};

此配置可以擴大你的初始打包文件 bundle.js ,我們推薦您采用動態(tài)引入的方式調用那些一開始并不會立刻被用到的代碼模塊挖函。

分離塊:例2

創(chuàng)建一個包含全部來自 node_modules 文件夾的模塊代碼 vendors 塊状植。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

這可能會產出一個包含所有第三方模塊庫的大包文件。我們推薦此包應只包含您框架的核心代碼和實用代碼部分怨喘,并且動態(tài)加載其余依賴津畸。

分離塊:例3

創(chuàng)建一個自定義 custom vendor 包,這個包只包含某些我們用正則式匹配的包必怜。

//**webpack.config.js**
module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'vendor',
          chunks: 'all',
        }
      }
    }
  }
};

這將使 react 和 react-dom 合并打包至同一獨立塊肉拓。如果你不確定某一塊里面都包含了哪些模塊的話,你應該去看一下 Bundle Analysis 去探究細節(jié)梳庆。

------------------- 結束線 -----------------------

本文翻譯結合筆者開發(fā)經驗暖途,如有錯誤卑惜,歡迎留言指正、探討驻售。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末露久,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子欺栗,更是在濱河造成了極大的恐慌毫痕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纸巷,死亡現(xiàn)場離奇詭異镇草,居然都是意外死亡,警方通過查閱死者的電腦和手機瘤旨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門梯啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人存哲,你說我怎么就攤上這事因宇。” “怎么了祟偷?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵察滑,是天一觀的道長。 經常有香客問我修肠,道長贺辰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任嵌施,我火速辦了婚禮饲化,結果婚禮上,老公的妹妹穿的比我還像新娘吗伤。我一直安慰自己吃靠,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布足淆。 她就那樣靜靜地躺著巢块,像睡著了一般。 火紅的嫁衣襯著肌膚如雪巧号。 梳的紋絲不亂的頭發(fā)上族奢,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音裂逐,去河邊找鬼歹鱼。 笑死,一個胖子當著我的面吹牛卜高,可吹牛的內容都是我干的弥姻。 我是一名探鬼主播南片,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庭敦!你這毒婦竟也來了疼进?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秧廉,失蹤者是張志新(化名)和其女友劉穎伞广,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體疼电,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡嚼锄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔽豺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片区丑。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖修陡,靈堂內的尸體忽然破棺而出沧侥,到底是詐尸還是另有隱情,我是刑警寧澤魄鸦,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布宴杀,位于F島的核電站,受9級特大地震影響拾因,放射性物質發(fā)生泄漏旺罢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一绢记、第九天 我趴在偏房一處隱蔽的房頂上張望主经。 院中可真熱鬧,春花似錦庭惜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砾跃,卻和暖如春骏啰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抽高。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工判耕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翘骂。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓壁熄,卻偏偏與公主長得像帚豪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子草丧,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容