(免責聲明:本譯文未參考任何其他文章,如有與其他譯版雷同部分,純屬巧合)
(聲明:中文版權歸作者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ā)經驗暖途,如有錯誤卑惜,歡迎留言指正、探討驻售。