最初,chunks(以及內(nèi)部導入的模塊)是通過內(nèi)部 webpack 圖譜中的父子關系關聯(lián)的。CommonsChunkPlugin 曾被用來避免他們之間的重復依賴,但是不可能再做進一步的優(yōu)化。
從 webpack v4 開始,移除了 CommonsChunkPlugin,取而代之的是 optimization.splitChunks。
開箱即用的 SplitChunksPlugin 對于大部分用戶來說非常友好。
默認情況下,它只會影響到按需加載的 chunks,因為修改 initial chunks 會影響到項目的 HTML 文件中的腳本標簽。
webpack 將根據(jù)以下條件自動拆分 chunks:
當嘗試滿足最后兩個條件時,最好使用較大的 chunks。
webpack 為希望對該功能進行更多控制的開發(fā)者提供了一組選項。
下面這個配置對象代表 SplitChunksPlugin 的默認行為。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
?string = '~'
?
默認情況下,webpack 將使用 chunk 的來源和名稱生成名稱(例如 vendors~main.js)。此選項使你可以指定用于生成名稱的分隔符。
?string = 'async' function (chunk)
?
這表明將選擇哪些 chunk 進行優(yōu)化。當提供一個字符串,有效值為 all,async 和 initial。設置為 all 可能特別強大,因為這意味著 chunk 可以在異步和非異步 chunk 之間共享。
請注意,它也應用于回退緩存組(splitChunks.fallbackCacheGroup.chunks).
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
},
},
};
或者,你也可以提供一個函數(shù)去做更多的控制。這個函數(shù)的返回值將決定是否包含每一個 chunk。
module.exports = {
//...
optimization: {
splitChunks: {
chunks(chunk) {
// exclude `my-excluded-chunk`
return chunk.name !== 'my-excluded-chunk';
},
},
},
};
?number = 30
?
按需加載時的最大并行請求數(shù)。
?number = 30
?
入口點的最大并行請求數(shù)。
[string] = ['javascript', 'unknown']
Sets the size types which are used when a number is used for sizes.
?number = 1
?
拆分前必須共享模塊的最小 chunks 數(shù)。
?boolean
?
為由 maxSize 分割的部分創(chuàng)建名稱時,阻止公開路徑信息。
?number = 20000 { [index: string]: number }
?
生成 chunk 的最小體積(以 bytes 為單位)。
?number { [index: string]: number }
?
生成 chunk 所需的主 chunk(bundle)的最小體積(以字節(jié)為單位)縮減。這意味著如果分割成一個 chunk 并沒有減少主 chunk(bundle)的給定字節(jié)數(shù),它將不會被分割,即使它滿足 splitChunks.minSize。
?number = 50000
?
強制執(zhí)行拆分的體積閾值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)將被忽略。
?number = 0
?
在 webpack 5 中引入了 splitChunks.minRemainingSize 選項,通過確保拆分后剩余的最小 chunk 體積超過限制來避免大小為零的模塊。 'development' 模式 中默認為 0。對于其他情況,splitChunks.minRemainingSize 默認為 splitChunks.minSize 的值,因此除需要深度控制的極少數(shù)情況外,不需要手動指定它。
?RegExp
? ?string
? ?function
?
按模塊層將模塊分配給緩存組。
?number = 0
?
使用 maxSize(每個緩存組 optimization.splitChunks.cacheGroups[x].maxSize 全局使用 optimization.splitChunks.maxSize 或?qū)髠渚彺娼M optimization.splitChunks.fallbackCacheGroup.maxSize 使用)告訴 webpack 嘗試將大于 maxSize 個字節(jié)的 chunk 分割成較小的部分。 這些較小的部分在體積上至少為 minSize(僅次于 maxSize)。 該算法是確定性的,對模塊的更改只會產(chǎn)生局部影響。這樣,在使用長期緩存時就可以使用它并且不需要記錄。maxSize 只是一個提示,當模塊大于 maxSize 或者拆分不符合 minSize 時可能會被違反。
當 chunk 已經(jīng)有一個名稱時,每個部分將獲得一個從該名稱派生的新名稱。 根據(jù) optimization.splitChunks.hidePathInfo 的值,它將添加一個從第一個模塊名稱或其哈希值派生的密鑰。
maxSize 選項旨在與 HTTP/2 和長期緩存一起使用。它增加了請求數(shù)量以實現(xiàn)更好的緩存。它還可以用于減小文件大小,以加快二次構(gòu)建速度。
?number
?
像 maxSize 一樣,maxAsyncSize 可以為 cacheGroups(splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize)或 fallback 緩存組(splitChunks.fallbackCacheGroup.maxAsyncSize )全局應用(splitChunks.maxAsyncSize)
maxAsyncSize 和 maxSize 的區(qū)別在于 maxAsyncSize 僅會影響按需加載 chunk。
?number
?
像 maxSize 一樣,maxInitialSize 可以對 cacheGroups(splitChunks.cacheGroups.{cacheGroup}.maxInitialSize)或 fallback 緩存組(splitChunks.fallbackCacheGroup.maxInitialSize)全局應用(splitChunks.maxInitialSize)。
maxInitialSize 和 maxSize 的區(qū)別在于 maxInitialSize 僅會影響初始加載 chunks。
?boolean = false function (module, chunks, cacheGroupKey) => string
? ?string
?
每個 cacheGroup 也可以使用:splitChunks.cacheGroups.{cacheGroup}.name。
拆分 chunk 的名稱。設為 false 將保持 chunk 的相同名稱,因此不會不必要地更改名稱。這是生產(chǎn)環(huán)境下構(gòu)建的建議值。
提供字符串或函數(shù)使你可以使用自定義名稱。指定字符串或始終返回相同字符串的函數(shù)會將所有常見模塊和 vendor 合并為一個 chunk。這可能會導致更大的初始下載量并減慢頁面加載速度。
如果你選擇指定一個函數(shù),則可能會發(fā)現(xiàn) chunk.name 和 chunk.hash 屬性(其中 chunk 是 chunks 數(shù)組的一個元素)在選擇 chunk 名時特別有用。
如果 splitChunks.name 與 entry point 名稱匹配,entry point 將被刪除。
main.js
import _ from 'lodash';
console.log(_.join(['Hello', 'webpack'], ' '));
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
// cacheGroupKey here is `commons` as the key of the cacheGroup
name(module, chunks, cacheGroupKey) {
const moduleFileName = module
.identifier()
.split('/')
.reduceRight((item) => item);
const allChunksNames = chunks.map((item) => item.name).join('~');
return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
},
chunks: 'all',
},
},
},
},
};
使用以下 splitChunks 配置來運行 webpack 也會輸出一組公用組,其下一個名稱為:commons-main-lodash.js.e7519d2bb8777058fa27.js(以哈希方式作為真實世界輸出示例)。
?boolean = true
?
弄清哪些 export 被模塊使用,以混淆 export 名稱,省略未使用的 export,并生成有效的代碼。 當它為 true 時:分析每個運行時使用的出口,當它為 "global" 時:分析所有運行時的全局 export 組合)。
緩存組可以繼承和/或覆蓋來自 splitChunks.* 的任何選項。但是 test、priority 和 reuseExistingChunk 只能在緩存組級別上進行配置。將它們設置為 false以禁用任何默認緩存組。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false,
},
},
},
};
?number = -20
?
一個模塊可以屬于多個緩存組。優(yōu)化將優(yōu)先考慮具有更高 priority(優(yōu)先級)的緩存組。默認組的優(yōu)先級為負,以允許自定義組獲得更高的優(yōu)先級(自定義組的默認值為 0)。
?boolean = true
?
如果當前 chunk 包含已從主 bundle 中拆分出的模塊,則它將被重用,而不是生成新的模塊。這可能會影響 chunk 的結(jié)果文件名。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
reuseExistingChunk: true,
},
},
},
},
};
?function
? ?RegExp
? ?string
?
允許按模塊類型將模塊分配給緩存組。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
json: {
type: 'json',
},
},
},
},
};
?function (module, { chunkGraph, moduleGraph }) => boolean
? ?RegExp
? ?string
?
控制此緩存組選擇的模塊。省略它會選擇所有模塊。它可以匹配絕對模塊資源路徑或 chunk 名稱。匹配 chunk 名稱時,將選擇 chunk 中的所有模塊。
為 ?{cacheGroup}.test
? 提供一個函數(shù):
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
svgGroup: {
test(module) {
// `module.resource` contains the absolute path of the file on disk.
// Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
const path = require('path');
return (
module.resource &&
module.resource.endsWith('.svg') &&
module.resource.includes(`${path.sep}cacheable_svgs${path.sep}`)
);
},
},
byModuleTypeGroup: {
test(module) {
return module.type === 'javascript/auto';
},
},
},
},
},
};
為了查看 module and chunks 對象中可用的信息,你可以在回調(diào)函數(shù)中放入 debugger; 語句。然后 以調(diào)試模式運行 webpack 構(gòu)建 檢查 Chromium DevTools 中的參數(shù)。
向 ?{cacheGroup}.test
? 提供 RegExp:
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
// Note the usage of `[\\/]` as a path separator for cross-platform compatibility.
test: /[\\/]node_modules[\\/]|vendor[\\/]analytics_provider|vendor[\\/]other_lib/,
},
},
},
},
};
?string
? ?function (pathData, assetInfo) => string
?
僅在初始 chunk 時才允許覆蓋文件名。 也可以在 output.filename 中使用所有占位符。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: '[name].bundle.js',
},
},
},
},
};
若為函數(shù),則:
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: (pathData) => {
// Use pathData object for generating filename string based on your requirements
return `${pathData.chunk.name}-bundle.js`;
},
},
},
},
},
};
通過提供以文件名開頭的路徑 ?'js/vendor/bundle.js'
?,可以創(chuàng)建文件夾結(jié)構(gòu)。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: 'js/[name]/bundle.js',
},
},
},
},
};
?boolean = false
?
告訴 webpack 忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 選項,并始終為此緩存組創(chuàng)建 chunk。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
enforce: true,
},
},
},
},
};
?string
?
設置 chunk id 的提示。 它將被添加到 chunk 的文件名中。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
idHint: 'vendors',
},
},
},
},
};
// index.js
import('./a'); // dynamic import
// a.js
import 'react';
//...
結(jié)果: 將創(chuàng)建一個單獨的包含 react 的 chunk。在導入調(diào)用中,此 chunk 并行加載到包含 ./a 的原始 chunk 中。
為什么:
這背后的原因是什么?react 可能不會像你的應用程序代碼那樣頻繁地更改。通過將其移動到單獨的 chunk 中,可以將該 chunk 與應用程序代碼分開進行緩存(假設你使用的是 chunkhash,records,Cache-Control 或其他長期緩存方法)。
// 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
//...
結(jié)果: 將創(chuàng)建一個單獨的 chunk,其中包含 ./helpers 及其所有依賴項。在導入調(diào)用時,此 chunk 與原始 chunks 并行加載。
為什么:
將 helpers 的內(nèi)容放入每個 chunk 中將導致其代碼被下載兩次。通過使用單獨的塊,這只會發(fā)生一次。我們會進行額外的請求,這可以視為一種折衷。這就是為什么最小體積為 30kb 的原因。
創(chuàng)建一個 commons chunk,其中包括入口(entry points)之間所有共享的代碼。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2,
},
},
},
},
};
創(chuàng)建一個 vendors chunk,其中包括整個應用程序中 node_modules 的所有代碼。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
創(chuàng)建一個 custom vendor chunk,其中包含與 RegExp 匹配的某些 node_modules 包。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
},
};
更多建議: