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