[转] Webpack 插件 — SplitChunksPlugin
默认值
开箱即用的 SplitChunksPlugin
应该对大多数用户都很好用。
默认情况下,它只影响随需应变的块,因为更改初始块会影响运行项目时包含的应有脚本标记 HTML 文件。
webpack 将根据以下条件自动分割块:
- 新块可被共享的,或者来自
node_modules
文件夹 - 新块将大于 30kb (在 min+gz 之前)
- 按需加载块时,并行请求的最大数量将小于或等于 5
- 初始页面加载时并行请求的最大数量将小于或等于 3
当试图满足后两个条件时,更大的块是首选。
配置
webpack为希望对该功能有更多控制的开发人员提供了一组选项。
选择默认配置是为了适应 web 性能最佳实践,但是不同项目的最佳策略可能有所不同。如果您正在更改配置,您应该度量更改的影响,以确保有真正的好处。
optimization.splitChunks
下面这个对象表示 SplitChunksPlugin
的默认行为:
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 将使用块的来源和名称生成名称(例如,vendors~main.js
)。此选项允许您指定用于生成名称的分隔符。
译者注:来源是指,这个块是来源于哪一个入口文件,如果是多入口公用,那么名字会加上这几个入口的名字:
entry: { polyfill: './src/utils/polyfill.js', main: './src/main.js', app: './src/index.js', } 复制代码
三个入口均引入了
vue
模块,最后打包的结果文件为vendors~app~main~polyfill.8fe99da0cdc25ac2ef2f.bundle
splitChunks.chunks
function (chunk) | string
该选项表示将选择哪些块进行优化。当提供一个字符串时,有效值为 all
、async
和 initial
。all
的 功能特别强大,因为它意味着即使在异步块和非异步块之间也可以共享块。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
// 包含所有类型的块
chunks: 'all'
}
}
};
复制代码
或者,您可以提供一个函数来进行更多的控制。返回值将指示是否包含每个块。
module.exports = {
//...
optimization: {
splitChunks: {
chunks (chunk) {
// 排除 `my-excluded-chunk`
return chunk.name !== 'my-excluded-chunk';
}
}
}
};
复制代码
您可以将此配置与 HtmlWebpackPlugin 组合使用。它将为您注入所有生成的 vendor块。
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
时,可以不遵循 maxSize
。
当块已经有名称时,每个部分将从该名称派生出一个新名称。取决于 optimization.splitChunks.hidePathInfo
,它将添加从第一个模块名或它的散列派生的键。
maxSize
选项的目是用于 HTTP/2 和长期缓存。它增加了请求数,以便更好地缓存。它还可以用来减小文件大小,以便更快地重新构建。
maxSize
比maxInitialRequest/maxAsyncRequests
具有更高的优先级。实际优先级是maxInitialRequest/maxAsyncRequests < maxSize < minSize
。
splitChunks.name
boolean: true | function (module, chunks, cacheGroupKey) | string
分割块的名称。提供 true
将根据块和缓存组键自动生成名称。提供字符串或函数将允许您使用自定义名称。如果名称与入口点名称匹配,则将删除入口点。
建议为生产构建将
splitChunks.name
设置为false
,这样就不会不必要地更改名称。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
name (module, chunks, cacheGroupKey) {
// generate a chunk name...
return; //...
}
}
}
};
复制代码
当为不同的分割块分配相同的名称时,所有供应商模块都被放置到一个共享块中,但不建议这样做,因为这会导致下载更多的代码。
splitChunks.cacheGroups
缓存组可以继承和/或覆盖 splitChunks.*
中的任何选项。但是 test
、priority
和 reuseExistingChunk
只能在缓存组级别配置。若要禁用任何默认缓存组,请将它们设置为 false
。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false
}
}
}
};
复制代码
splitChunks.cacheGroups.priority
number
一个模块可以属于多个缓存组。优化将优先选择具有较高 priority
的缓存组。默认组具有负优先级,以允许自定义组具有更高的优先级(自定义组的默认值为 0
)。
optimization: {
splitChunks: {
//...
cacheGroups: {
vendors1: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
vendors2: {
test: /[\\/]node_modules[\\/]/,
priority: 1
}
}
}
}
复制代码
node_modules
的内容会被打包到的 vendors2 中
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
选项,并始终为这个缓存组创建块。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
enforce: true
}
}
}
}
};
复制代码
示例
Defaults: 示例 1
// index.js
import('./a'); // 动态导入
复制代码
// a.js
import 'react';
//...
复制代码
Result: 将创建一个包含 react
的单独块。在导入调用时,这个块与包含 ./a
的原始块并行加载。
Why:
- 条件 1: 块包含来自
node_modules
的模块 - 条件 2:
react
大于 30kb - 条件 3: 导入调用的并行请求数为 2
- 条件 4: 在初始页面加载时不影响请求
这背后的原因是什么?react
可能不会像应用程序代码那样频繁更改。通过将它移动到一个单独的块中,这个块可以与应用程序代码分开缓存(假设您使用的是 chunkhash、records、Cache-Control 或其他长期缓存方法)。
Defaults: 示例 2
// entry.js
// 动态导入
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
//...
复制代码
Result: 将创建一个包含 ./helpers
及其所有依赖项的单独块。在导入调用时,这个块与原始块并行加载。
Why:
- 条件 1: 这个块在两个导入调用之间共享
- 条件 2:
helpers
超过 30kb - 条件 3: 导入调用的并行请求数为 2
- 条件 4: 在初始页面加载时不影响请求
将 helpers
的内容放入每个块中,将导致其代码被下载两次。通过使用单独的块,这种情况只会发生一次。我们支付额外请求的成本,这可以看作是一种权衡。这就是最小大小为 30kb 的原因。
Split Chunks: 示例 1
创建一个 commons
块,其中包括入口点之间共享的所有代码。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2
}
}
}
}
};
复制代码
此配置会扩大初始包,建议在不立即需要模块时使用动态导入。
Split Chunks: 示例 2
创建一个 vendors
块,其中包含整个应用程序中来自 node_modules
的所有代码。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
复制代码
这可能导致生成一个包含所有外部包的大块。建议只包含核心框架和实用程序,并动态加载其余依赖项。
Split Chunks: 示例 3
创建一个自定义 vendor
块,其中包含与 RegExp
匹配的某些 node_modules
包。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
}
};
复制代码
这将导致将
react
和react-dom
分割成单独的块。如果不确定块中包含了哪些包,可以参考 Bundle Analysis 部分了解详细信息。