力扣笔试题-Webpack的代码切割和长效化缓存(3版和4版)
前言
leetcode有道题叫用 Webpack 实现 predictable long term cache
, 点击访问 🔗
predictable long term cache
中文翻译就是可预测的长效缓存
, 说白了就是代码切割(Code Splitting
), 通过代码切割的方式细化打包规则, 并用文件文本hash值标记文件, 当文件修改重新打包时会修改hash值, 否则文件内容与文件名不变, 以达到浏览器长期缓存的效果.
Webpack 3 和 Webpack 4 实现 Code Splitting 的区别
Webpack 3 是通过在plugins
下添加插件webpack.optimize.CommonsChunkPlugin
与entry
的配合实现
Webpack 4 经过改版后将Code Splitting直接通过配置原生API实现, 对应APIoptimization.splitChunks
对比Webpack 3 和 Webpack 4 的实现方式, Webpack 4 更明确更简单也更好配置, 可以看看他们的具体实现
通过Webpack 3 实现 Code Splitting
我们看下一个简单的Webpack 3 的示例, 只留下entry 和 plugins
{
entry: {
chunks-name: ['npm package']
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(config)
]
}
假设我们要将echarts
模块单独生成一个echarts文件, 将react
、react-dom
、react-redux
等react相关包生成react文件, 其它从/node_modules/
出来的模块打包成common文件, app文件是js入口文件应排除react文件、echarts文件和common文件代码, 我们可以这样配置:
{
// entry 里的每一项都是一个可打包文件
entry: {
// app是js主文件, 也是js的入口文件
app: [require.resolve('./polyfills'), paths.appIndexJs],
// echarts模块单独打包成echarts文件
echarts: ['echarts'],
// react、react-dom、react-redux模块打包成react文件
react: ['react', 'react-dom', 'react-redux']
},
plugins: [
// 将entry里的文件以最小化关联形式打包
new webpack.optimize.CommonsChunkPlugin({
name: ['echarts', 'react'],
minChunks: Infinity,
}),
// 从./node_modules文件夹里取的其它模块打包到common文件
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
minChunks: module =>
module.resource
&& /\.js$/.test(module.resource)
&& module.resource.indexOf(path.join(process.cwd(), './node_modules')) === 0,
chunks: ['app'],
}),
// manifest文件起到对js文件的映射关系
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['react', 'common', 'echarts', 'app'],
})
]
}
通过Webpack 4 实现 Code Splitting
Webpack 4 映入内置的splitChunks API配置起来会简单很多, 以leetcode笔试题为例:
题目叙述
文件目录 🔗如下:
.
├── package.json
├── src
│ ├── common
│ │ └── index.js
│ ├── index.js
│ ├── module-1
│ │ └── index.js
│ └── module-2
│ └── index.js
├── webpack.config.js
配置Webpack后打包输出如下:
Asset Size Chunks Chunk Names
common~main.438f8dd8.js 201 bytes 0 [emitted] common~main
main.9d46ca0f.js 170 bytes 1 [emitted] main
module1~main.01269684.js 212 bytes 2 [emitted] module1~main
module2~main.177226e7.js 214 bytes 3 [emitted] module2~main
runtime~main.04a66190.js 1.46 KiB 4 [emitted] runtime~main
vendors~main.62d56e50.js 69.7 KiB 5 [emitted] vendors~main
修改common/index.js
, 将三个!!!去掉两个重新打包, common-main文件的hash值了, 其它文件hash值没变
根据题意我们可以直接使用optimization.splitChunks.cacheGroups
配置, 配置代码如下:
{
optimization: {
// 通过runtimeChunk API自动生成runtime文件
runtimeChunk: true,
splitChunks: {
// 设置最大生成的请求文件数, 默认为3, 这个必须设置, 要不然就只会生成3个文件
maxInitialRequests: 10,
// cacheGroups是splitChunks配置的关键, 很灵活
cacheGroups: {
// 生成vendors~main文件
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors~main',
chunks: 'all'
},
// 生成common~main文件
commons: {
name: 'common~main',
test: /src\/common/,
chunks: 'initial',
priority: 10,
minSize: 0,
},
// 生成module1~main和module2~main文件
module: {
name: module => module.context.split('/').pop().replace('-', '') + '~main',
test: /src\/module-/,
chunks: 'initial',
priority: 10,
minSize: 0,
},
},
},
},
}
Webpack 的代码比较含蓄API众多, 中文翻译总是会慢两拍, 刚开始博主没有设置maxInitialRequests值总是打包不出对应的文件数量, 最后通过Webpack的源码跟踪才找到的答案, 我们可以看看关键代码:
通过代码跟踪我们发现node_modules/webpack/lib/SplitChunksPlugin.js
的703行是关键代码, 它通过filter函数过滤掉, 使我们的打包工作不顺利, 摘录代码:
validChunks = validChunks.filter(chunk => {
// respect max requests when not enforced
const maxRequests = chunk.isOnlyInitial()
? item.cacheGroup.maxInitialRequests
: chunk.canBeInitial()
? Math.min(
item.cacheGroup.maxInitialRequests,
item.cacheGroup.maxAsyncRequests
)
: item.cacheGroup.maxAsyncRequests;
return (
!isFinite(maxRequests) || getRequests(chunk) < maxRequests
);
});
当我们在cacheGroups的每一项里没有传maxInitialRequests, 则默认区的optimization.splitChunks.maxInitialRequests
, 当没设置maxInitialRequests时maxRequests = item.cacheGroup.maxInitialRequests =mization.splitChunks.maxInitialRequests = 3
, 因此getRequests(chunk) < maxRequests
总是能返回false来控制chunk的数量
总结
通过Webpack 3 和 Webpack 4 对code spliting的配置实现predictable long term cache, Webpack 4 通过直接修改splitChunks API也更简单明了, 但由于splitChunks API里增加了很多配置参数不易理解, 可读性和易用性个人感觉不如Webpack 3.