[转] 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

该选项表示将选择哪些块进行优化。当提供一个字符串时,有效值为 allasyncinitialall 的 功能特别强大,因为它意味着即使在异步块和非异步块之间也可以共享块。

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 和长期缓存。它增加了请求数,以便更好地缓存。它还可以用来减小文件大小,以便更快地重新构建。

maxSizemaxInitialRequest/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.* 中的任何选项。但是 testpriorityreuseExistingChunk 只能在缓存组级别配置。若要禁用任何默认缓存组,请将它们设置为 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.maxAsyncRequestssplitChunks.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‘, } } } } }; 复制代码

这将导致将 reactreact-dom 分割成单独的块。如果不确定块中包含了哪些包,可以参考 Bundle Analysis 部分了解详细信息。

 

相关文章