最初,块(以及其中导入的模块)通过内部 webpack 图表中的父子关系连接。CommonsChunkPlugin
用于避免它们之间出现重复的依赖项,但无法进行进一步的优化。
自 webpack v4 起,CommonsChunkPlugin
已被 optimization.splitChunks
取代。
开箱即用的 SplitChunksPlugin
应该可以很好地满足大多数用户的需求。
默认情况下,它只影响按需块,因为更改初始块会影响 HTML 文件应包含以运行项目的脚本标记。
Webpack 将根据以下条件自动拆分块
node_modules
文件夹在尝试满足最后两个条件时,较大的块更受青睐。
Webpack 为希望更多地控制此功能的开发人员提供了一组选项。
此配置对象表示 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 将使用块的来源和名称生成名称(例如 vendors~main.js
)。此选项允许你指定要用于生成名称的分隔符。
string = 'async'
function (chunk)
RegExp
这表示将选择哪些块进行优化。当提供字符串时,有效值为 all
、async
和 initial
。提供 all
可能特别有用,因为它意味着即使在异步和非异步块之间也可以共享块。
请注意,它也适用于后备缓存组(splitChunks.fallbackCacheGroup.chunks
)。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
},
},
};
或者,你可以提供一个函数以获得更多控制。返回值将指示是否包含每个块。
module.exports = {
//...
optimization: {
splitChunks: {
chunks(chunk) {
// exclude `my-excluded-chunk`
return chunk.name !== 'my-excluded-chunk';
},
},
},
};
如果你使用的是 webpack 5.86.0 或更高版本,你还可以传递正则表达式
module.exports = {
//...
optimization: {
splitChunks: {
chunks: /foo/,
},
},
};
number = 30
按需加载时的最大并行请求数。
number = 30
入口点的最大并行请求数。
[string] = ['javascript', 'unknown']
当数字用于大小时,设置所使用的尺寸类型。
number = 1
在拆分之前,模块必须在块之间共享的最小次数。
boolean
在创建由 maxSize 拆分的部件的名称时,防止公开路径信息。
number = 20000
{ [index: string]: number }
要生成的块的最小大小(以字节为单位)。
number
{ [index: string]: number }
要生成的块所需的主块(包)的最小大小减小(以字节为单位)。这意味着,如果拆分为块不会将主块(包)的大小减少给定的字节数,则不会拆分,即使它满足 splitChunks.minSize
值也是如此。
splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold
number = 50000
强制拆分的尺寸阈值,并忽略其他限制(minRemainingSize、maxAsyncRequests、maxInitialRequests)。
splitChunks.cacheGroups.{cacheGroup}.minRemainingSize
number = 0
在 webpack 5 中引入了 splitChunks.minRemainingSize
选项,通过确保拆分后剩余块的最小大小高于限制值,来避免出现大小为零的模块。在 “开发”模式 下,默认为 0
。对于其他情况,splitChunks.minRemainingSize
默认为 splitChunks.minSize
的值,因此除了在需要深度控制的罕见情况下,无需手动指定。
splitChunks.cacheGroups.{cacheGroup}.layer
RegExp
string
function
按模块层将模块分配到缓存组。
number = 0
使用 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 和长期缓存一起使用。它增加了请求计数以实现更好的缓存。它还可用于减小文件大小以加快重建速度。
number
与 maxSize
类似,maxAsyncSize
可以全局应用(splitChunks.maxAsyncSize
),应用于缓存组(splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize
),或应用于回退缓存组(splitChunks.fallbackCacheGroup.maxAsyncSize
)。
maxAsyncSize
和 maxSize
之间的区别在于,maxAsyncSize
仅会影响按需加载的块。
number
与 maxSize
类似,maxInitialSize
可以全局应用(splitChunks.maxInitialSize
),应用于缓存组(splitChunks.cacheGroups.{cacheGroup}.maxInitialSize
),或应用于回退缓存组(splitChunks.fallbackCacheGroup.maxInitialSize
)。
maxInitialSize
和 maxSize
的区别在于 maxInitialSize
仅会影响初始加载块。
boolean = false
function (module, chunks, cacheGroupKey) => string
string
每个 cacheGroup 也可以使用:splitChunks.cacheGroups.{cacheGroup}.name
。
拆分块的名称。提供 false
将保持块的相同名称,因此它不会不必要地更改名称。这是生产构建的推荐值。
提供字符串或函数允许你使用自定义名称。指定一个字符串或始终返回相同字符串的函数会将所有通用模块和供应商合并到一个块中。这可能会导致更大的初始下载并减慢页面加载速度。
如果你选择指定一个函数,你可能会发现 chunk.name
属性(其中 chunk
是 chunks
数组的一个元素)在为你的块选择名称时特别有用。
如果 splitChunks.name
匹配一个入口点名称,则会删除该入口点。
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
的组的块(哈希作为真实世界输出的一个示例)。
splitChunks.cacheGroups{cacheGroup}.usedExports
boolean = true
了解模块使用的哪些导出以混淆导出名称、忽略未使用的导出并生成更高效的代码。当其为 true
时:分析每个运行时使用的导出,当其为 "global"
时:分析所有运行时的导出。
缓存组可以继承和/或覆盖 splitChunks.*
中的任何选项;但 test
、priority
和 reuseExistingChunk
只能在缓存组级别配置。要禁用任何默认缓存组,请将它们设置为 false
。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false,
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.priority
number = -20
一个模块可以属于多个缓存组。优化将优先使用具有较高 priority
的缓存组。默认组具有负优先级,以允许自定义组采用较高优先级(自定义组的默认值为 0
)。
splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
boolean = true
如果当前块包含已从主包中拆分的模块,它将被重用,而不是生成一个新的块。这可能会影响块的结果文件名。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
reuseExistingChunk: true,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.type
function
RegExp
string
允许按模块类型将模块分配给缓存组。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
json: {
type: 'json',
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.test
function (module, { chunkGraph, moduleGraph }) => boolean
RegExp
string
控制此缓存组选择哪些模块。省略它将选择所有模块。它可以匹配绝对模块资源路径或块名称。当匹配块名称时,将选择块中的所有模块。
向 {cacheGroup}.test
提供函数
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
和 chunks
对象中有哪些可用信息,您可以在回调中放置 debugger;
语句。然后 在调试模式下运行 webpack 构建 以在 Chromium DevTools 中检查参数。
向 {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/,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.filename
字符串
函数 (pathData, assetInfo) => 字符串
仅当它是初始块时才允许覆盖文件名。在 output.filename
中可用的所有占位符在此处也可用。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: '[name].bundle.js',
},
},
},
},
};
作为函数
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'
。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: 'js/[name]/bundle.js',
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.enforce
布尔值 = false
告诉 webpack 忽略 splitChunks.minSize
、splitChunks.minChunks
、splitChunks.maxAsyncRequests
和 splitChunks.maxInitialRequests
选项,并始终为该缓存组创建块。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
enforce: true,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.idHint
字符串
设置块 ID 的提示。它将被添加到块的文件名中。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
idHint: 'vendors',
},
},
},
},
};
// index.js
import('./a'); // dynamic import
// a.js
import 'react';
//...
结果:将创建一个单独的块,其中包含 react
。在导入调用时,此块与包含 ./a
的原始块并行加载。
原因
node_modules
的模块react
大于 30kb背后的原因是什么?react
可能不会像你的应用程序代码那样经常更改。通过将其移到单独的块中,可以将此块与你的应用程序代码分开缓存(假设你正在使用 chunkhash、记录、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
//...
结果:将创建一个单独的块,其中包含 ./helpers
及其所有依赖项。在导入调用时,此块将与原始块并行加载。
原因
helpers
大于 30kb将 helpers
的内容放入每个块中会导致其代码被下载两次。通过使用单独的块,这只会发生一次。我们支付了额外请求的费用,这可以被视为一种权衡。这就是为什么有 30kb 的最小大小。
创建一个 commons
块,其中包括在入口点之间共享的所有代码。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2,
},
},
},
},
};
创建一个 vendors
块,其中包括整个应用程序中来自 node_modules
的所有代码。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
创建一个 custom vendor
块,其中包含由 RegExp
匹配的特定 node_modules
包。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
},
};