SplitChunksPlugin是webpack4中官方plugin,用于做分包打包的,可以帮你把重复引入的模块按规则打包到指定的js里面。以下是SplitChunksPlugin的默认配置:
splitChunks: {
chunks: "async", //在默认情况下,SplitChunksPlugin 仅仅影响按需加载的代码块,如果需要对同步的代码做代码分割打包,那么chunk配置为'all'
minSize: 30000, // 模块的最小体积
minChunks: 1, // 模块的最小被引用次数
maxAsyncRequests: 5, // 按需加载的最大并行请求数
maxInitialRequests: 3, // 一个入口最大并行请求数
automaticNameDelimiter: '~', // 文件名的连接符
name: true,
cacheGroups: { // 缓存组
vendors: { //引入的node modules里面的库打包成一个vendor
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: { //其他重复的模块,如业务组件、自定义公共组件等
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
在单一入口页的项目开启splitChunk的功能其实配置很简单,只需要在 optimization 里面配置如下,splitChunk会使用默认的配置对项目进行拆包
splitChunks: {
chunks: 'all',
name: true
}
一般我们配置多入口页,都会使用 HtmlWebpackPlugin ,如果是 HtmlWebpackPlugin 结合SplitChunkPlugin 使用的话,需要在不同的入口页面指明该页面对应的chunk的名称,否则该页面引用的chunk文件对应不上的话,页面是无法正常加载的
下图是我的项目结构,app.js是主入口页,其他几个入口页都是分享出去的页面,对应app.js里面的一些模块(app.js里面的模块采取异步加载的方式引入)
如果不使用 SplitChunksPlugin ,我们利用 BundleAnalyzerPlugin 打包分析工具查看打包后的结果(下图),非常大,很多公共的库或业务组件被重复打包
接着,我们按SplitChunksPlugin的基础配置,在webpack.config.js里面引入,以~连接的chunk就是SplitChunksPlugin使用基础的配置给我们拆包出来的文件,这时我们的页面是不能正常加载的,因为这些拆包出来的chunk不能自动地注入到所引用的入口页里
那么我们先把项目里引用的node_modules的三方库拆出来,并在入口文件中注入,SplitChunksPlugin配置如下:
splitChunks: {
chunks: 'all',
name: true,
cacheGroups: {
vendors: { // 项目基本框架等
chunks: 'all',
test: /node_modules/,
priority: 100,
name: 'vendors',
}
}
}
HtmlWebpackPlugin配置:
new HtmlWebpackPlugin({
template: './templete/index.html',
filename: 'index.html',
chunks: ['app', 'vendors'] //注入对应的入口js文件、拆包出来的verdor文件(命名跟SplitChunks cacheGroup里配置的名称一致)
}),
new HtmlWebpackPlugin({
template: './templete/index-no-pb.html',
filename: 'home.html',
chunks: ['home', 'vendors']
}),
new HtmlWebpackPlugin({
template: './templete/index-no-pb.html',
filename: 'profile.html',
chunks: ['profile', 'vendors']
}),
...
我们可以看到,除了公共库的chunk,webpack还帮我们打包出了一些其他的公共chunk,这是因为默认配置里还有个default参数,会帮我们打包一些别的重复引用的模块。同样,我们还是需要给这些打包的chunk配置一下,不然这些公共chunk也不能被正常地注入
修改一下SplitChunksPlugin的配置
splitChunks: {
chunks: 'all',
name: true,
cacheGroups: {
vendors: { // 项目引用的三方库,如mobx等
chunks: 'all',
test: /node_modules/,
priority: 100, //值越大,打包越优先参照此配置项
name: 'vendors',
},
commons: { // 其他同步加载公共包,如自定义组件等
chunks: 'all',
minChunks: 2,
priority: 80,
name: 'commons'
}
}
}
重写了splitChunks 的 cacheGroups 默认参数后,可以按照我们的要求进行打包了,node_modules里面的部分打包到vendor.chunk里面,自定义的组件等其他重复引用模块被打包到了commons.chunk里,注意在HtmlWebpackPlugin 里面也要同时配置commons这个chunk
其实,按上面的配置,我们的打包结果已经比之前优化很多了,但是,这并不是最好的处理结果。因为commons.chunk里面打包的重复模块,在不同的入口页其实是用不上的。commons.chunk打包了所有引用超过两次的模块,其中就包括record chunk、profile chunk、home chunk、rank chunk等,因为这些部分同时在app.js和对应的入口页中引用了。比如说我只打开profile.html入口页,那我只需要profile chunk,而不需要另外的一些模块。那我们再修改一下配置
splitChunks: {
chunks: 'all',
name: true,
cacheGroups: {
vendors: { // 项目引用的三方库,如mobx等
chunks: 'all',
test: /node_modules/,
priority: 100,
name: 'vendors',
},
commons: { // 其他同步加载公共包,如自定义组件等
chunks: 'all',
minChunks: 3, //最小引用次数修改为3,那么app.js和其他入口页共用的模块就不会被打包进来,而是只打包了引用次数超过三次的一些自定义组件
name: 'commons',
priority: 80,
},
default: false //注意把cacheGroups的默认的default设置为false,不然还是会走default里面的配置,将引用了两次以上的模块进行打包
}
}
最后结果如下图所示,公共包有两个,而业务逻辑模块(home|rank|record|profile)没有被打进commons.chunk里面,被单独打成了一个个异步chunk。
虽然最后的打包方式整体大小会比之前的稍大一点,但是在请求不同的入口页时,会按需引入,以record.js入口页为例,请求时加载vendor + commons + record + recordChunk(约等于930多k),但是按之前的方式全部打到一个commons.chunk里,请求时加载vendor + commons + record(约等于1019多k)
SplitChunksPlugin虽然好用,但是也不要盲目地使用,应该根据项目的具体情况而定,一般如果是单入口页面,就直接按默认配置即可;多入口页的话,可以根据具体情况再进行拆包,自己写cacheGroups的配置的时候注意要关闭或者覆盖默认配置。
【参考】
https://webpack.js.org/plugins/split-chunks-plugin/#defaults