webpack 4 废弃了之前的不怎么好用的 CommonsChunk,取而代之的是 SplitChunks。既然是后起之秀,那肯定是有进步的。不过话说回来,虽然是进步了不少,但是如果刚接触的话配置起来同样还是一头雾水。
首先 webpack 总共提供了三种办法来实现 Code Splitting,如下:
这里我们姑且只讨论使用 SplitChunks 抽取公有代码。
首先我们所说的 SplitChunks 是由 webpack 4 内置的 SplitChunksPlugin 插件提供的能力,可直接在 optimization
选项中配置,其默认配置如下:
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
}
}
}
}
};
参数说明如下:
上面的那么多参数,其实都可以不用管,cacheGroups 才是我们配置的关键。它可以继承/覆盖上面 splitChunks
中所有的参数值,除此之外还额外提供了三个配置,分别为:test
, priority
和 reuseExistingChunk
。
一般来说我们常用的配置都是 common + page 的形式。而 page 在 entry 入口的时候就已经配置好了。那么现在就只剩下 common 的处理,这里讨论几种方案:
下面我们把所有 node_modules 的模块被不同的 chunk 引入超过 1 次的抽取为 common。
cacheGroups: {
common: {
test: /[\\/]node_modules[\\/]/,
name: 'common',
chunks: 'initial',
priority: 2,
minChunks: 2,
},
}
或者干脆把所有模块被不同的 chunk 引入超过 1 次的抽取为 common。
cacheGroups: {
common: {
name: 'common',
chunks: 'initial',
priority: 2,
minChunks: 2,
},
}
比如我们想把一些基础的框架单独抽取如 react ,然后是业务的基础。
cacheGroups: {
reactBase: {
name: 'reactBase',
test: (module) => {
return /react|redux|prop-types/.test(module.context);
},
chunks: 'initial',
priority: 10,
},
common: {
name: 'common',
chunks: 'initial',
priority: 2,
minChunks: 2,
},
}
整个顺下来看起来好像是比较简单明了了,而且还提供了 test 和 chunks 来过滤筛选,一个字:很棒!但是如果要实现更精细的控制,就会发现还是有点问题。
以上面抽取 react 相关的基础库为例,简单的 test 匹配 react,误杀的太多,比如有些 react 组件命名就有 react 关键词,结果把这个也打进去了,肯定不是我们想要的。
于是就想到从 entry 入口里面定义一个 react 的基础库,核心代码如下:
const vendorPkg = [
'react',
'react-dom',
'redux',
'redux-thunk',
'react-redux',
'react-router-dom',
'react-router-redux',
'history',
'prop-types',
'react-loadable',
];
module.exports = {
entry: {
vendor: vendorPkg,
...
}
}
然后开始配置 cacheGroups
,发现不论怎么配置都实现不了其他页面共用这个 vendor。
同样对于通过 MiniCssExtractPlugin 生成的 CSS 文件也可以通过 SplitChunks 来进行抽取公有样式等。
如下表示将所有 CSS 文件打包为一个(注意将权重设置为最高,不然可能其他的 cacheGroups 会提前打包一部分样式文件):
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
priority: 20,
}
}
}
}
}