前言
从Webpack3到Webpack4一个指标性的变化就是Webpack3的CommonsChunkPlugin被废弃了,取而代之的是Webpack4中的SplitChunksPlugin,这不仅仅是plugin名称的变化,也是分割chunk思想的变化。两种plugin的不同可以参照以下这篇文章:
这篇文章的目的是记录一下对SplitChunksPlugin一些常用配置项的理解,通过某些配置项的用法来体会SplitChunksPlugin的分包思想。
工具: webpack-bundle-analyzer
该插件是将打包后的内容用canvas以图形的方式展示出来,借助这个工具,我们可以知道每个chunk由哪些模块组成,非常方便好用。配置如下:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
...
plugins: [
...
new BundleAnalyzerPlugin()
]
}
SplitChunksPlugin 默认配置及规则
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
}
}
}
}
};
默认规则(官网原文):
- New chunk can be shared OR modules are from the node_modules folder
- New chunk would be bigger than 30kb (before min+gz)
- Maximum number of parallel requests when loading chunks on demand
would be lower or equal to 5 - Maximum number of parallel requests at initial page load would be lower or equal to 3
翻译过来大致意思如下(可能有不准确的地方)
- 新代码块可以被共享引用,或这些模块都是来自node_modules
- 新产出的vendor-chunk的大小得大于30kb
- 按需加载的代码块(vendor-chunk)并行请求的数量小于或等于5个
- 初始加载的代码块,并行请求的数量小于或者等于3个
SplitChunksPlugin 最简单配置结果分析
module.exports = {
entry: [...],
mode: 'production', // development
module: {
rules: [...]
...
}
...
optimization: {
...
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name: 'runtime'
}
}
}
非常简单,两个地方:splitChunks和runtimeChunk。我们先用上面的配置走一个看看chunk的情况。
可以看出共分了5个chunk。
业务:index.js, main.js (二者无交集)
第三方库:vendors~index.js, vendors~main.js (二者有交集)(可以参考引用文章webpack4:连奏中的进化为什么会出现这种现象)
runtime: runtime.js
从chunk名称上可以看出vendors~index是index引用的第三方库,vendors~main是main引用的第三方库,事实上也是如此。
业务代码为什么会被分为main和index两部分
我觉得这和项目的技术构成有关:React + canvas。项目大部分组件是用React和相关技术写的。但是有一块需要画图的业务完全是用canvas及相关图形库来做的。从依赖上看,canvas与React是不相关的,完全没有必要去引用react以及相关的库。而canvas第三方库的大小又超过了30k。所以webpack在做split时会将这两部分业务以及依赖的库分开,这样对第三方库也就是vendors~chunk做到了按需加载。
模版index.html初始引用的script
打开生成的index.html,我们却只发现main和vendors~main,并没有发现index以及vendors~index。main出现在index.html很正常,因为我们项目的入口文件就打包在main里面。那么index.js去哪里了。
别着急,模版中引用了另外一个chunk即runtime.js,打开这个文件,我们会发现如下代码,可见业务2相关的代码由webpack4生成的runtime chunk来需要的时候动态引入(比如用户打开canvas)。
maxAsyncRequests
splitChunks配置规则中有一条:按需加载的代码块(vendor-chunk)并行请求的数量小于或等于5个
这句话具体是个什么意思?这句话其实就对应默认配置属性maxAsyncRequests。
maxAsyncRequests(最大的异步请求数)和maxInitialRequests(最大的初始请求数是为了防止chunk划分的过于细致,导致大量的文件请求降低performance)。
还从上一次分包情况入手分析:vendors~index和vendors~main都是来自node_moudules。那么当前按需加载的代码块的并行请求数就是2。
我们设置maxAsyncRequests=2,发现分包结果和之前相同(既然一样就不上图了)
我们再设置maxAsyncRequests=1,配置以及分包结果如下:
...
splitChunks: {
chunks: 'all',
maxAsyncRequests: 1
},
runtimeChunk: {
name: 'runtime'
}
....
可以看到,index和main还在,但是verdors只剩下了vendors~main chunk。可见按需加载(chunks on demand)的代码块指的就是vendors~chunk
maxInitialRequests
初始加载的代码块,并行请求的数量小于或者等于3个
这个是关于template index.html中放置的script数量。我们还是使用之前的分析方法:保持其他默认配置不变,我们分别将maxInitialRequests设置为2和1,两次index.html变化如下:
可以发现,当maxInitialRequests=1时,vendors~main没有了,事实上,vendors~main的内容全部合并到了main中。由此可以得出maxInitialRequests指的是模版html文件中,并行请求的javascript(不包括runtime)的数量。
chunks: all
请注意,上述配置中使用的chunks=’all’,其实这不是默认值,默认 entry 的 chunk 不会被拆分。chunks=’all’代表着我们拆分chunk时也包含entry。
做个实验验证一下:
splitChunks: {
// 默认值为3,我们再增加到10
maxInitialRequests: 10
}
我们发现,main作为entry并没有被拆分。
尾声:name
在使用splitChunks时,发现一个有意思的现象,权且记一下。
splitChunks: {
chunks: 'all',
name: 'vendor'
},
runtimeChunk: {
name: 'runtime'
}
设置splitChunks中的name,其他配置保持默认。发现本该正常生成的vendors~index和vendors~main都被合并到了vendor中。,