在 webpack 打包过程中,经常出现 vendor.js, app.js 单个文件较大的情况,这偏偏又是网页最先加载的文件,这就会使得加载时间过长,从而使得白屏时间过长,影响用户体验。所以我们需要有合理的分包策略。
CommonsChunkPlugin
在 Webapck4.x 版本之前,我们都是使用 CommonsChunkPlugin 去做分离
···<pre data-language="javascript" id="L4z5I" class="ne-codeblock" style="border: 1px solid #e8e8e8; border-radius: 2px; background: #f9f9f9; padding: 16px; font-size: 13px; color: #595959">plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
)
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: 'initial',
minChunks: 2,
}),
]</pre>
我们把以下文件单独抽离出来打包
- node_modules 文件夹下的,模块
- 被 3 个 入口 chunk 共享的模块
optimization.splitChunks
webpack 4 最大的改动就是废除了 CommonsChunkPlugin 引入了 optimization.splitChunks。如果你的 mode 是 production,那么 webpack4 就会自动开启 Code Splitting。
它内置的代码分割策略是这样的:
新的 chunk 是否被共享或者是来自 node_modules 的模块
新的 chunk 体积在压缩之前是否大于 30kb
按需加载 chunk 的并发请求数量小于等于 5 个
页面初始加载时的并发请求数量小于等于 3 个
虽然在 webpack4 会自动开启 Code Splitting,但是随着项目工程的最大,这往往不能满足我们的需求,我们需要再进行个性化的优化。
应用实例
我们先找到一个优化空间较大的项目来进行操作。这是一个后台管理系统项目,大部分内容由 3-4 个前端开发,平时开发周期较短,且大部分人没有优化意识,只是写好业务代码完成需求,日子一长,造成打包出来的文件较大,大大影响性能。
我们先用 webpack-bundle-analyzer 分析打包后的模块依赖及文件大小,确定优化的方向在哪。
![tps://upload-images.jianshu.io/upload_images/6334858-ef229e41ef621599.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后我们再看下打包出来的 js 文件
![image](https://upload-images.jianshu.io/upload_images/6334858-bb256a2faa48ca7b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
看到这两张图的时候,我内心是崩溃的,槽点如下
打包后生成多个将近 1M 的 js 文件,其中不乏 vendor.js 首页必须加载的大文件
xlsx.js 这样的插件没必要使用,导出 excel 更好的方法应该是后端返回文件流格式给前端处理
echart 和 iview 文件太大,应该使用 cdn 引入的方法
吐槽完之后我们就要开始做正事了。正是因为有这么多槽点,我们才更好用来验证我们优化方法的可行性。
抽离 echart 和 iview
由上面分析可知,echart 和 iview 文件太大,此时我们就用到 webpack4 的 optimization.splitChunks 进行代码分割了,把他们单独抽离打包成文件。(为了更好地呈现优化效果,我们先把 xlsx.js 去掉)
vue.config.js 修改如下:
<pre data-language="javascript" id="uP8eu" class="ne-codeblock" style="border: 1px solid #e8e8e8; border-radius: 2px; background: #f9f9f9; padding: 16px; font-size: 13px; color: #595959">chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial'
},
iview: {
name: 'chunk-iview',
priority: 20,
test: /[\\/]node_modules[\\/]_?iview(.*)/
},
echarts: {
name: 'chunk-echarts',
priority: 20,
test: /[\\/]node_modules[\\/]_?echarts(.*)/
},
commons: {
name: 'chunk-commons',
minChunks: 2,
priority: 5,
chunks: 'initial',
reuseExistingChunk: true
}
}
})
},
</pre>
此时我们再用 webpack-bundle-analyzer 分析一下
![image](https://upload-images.jianshu.io/upload_images/6334858-c62a3418b9d236a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
打包出来的 js 文件
从这里可以看出我们已经成功把 echart 和 iview 单独抽离出来了,同时 vendor.js 也相应地减小了体积。此外,我们还可以继续抽离其他更多的第三方模块。
CDN 方式
虽然第三方模块是单独抽离出来了,但是在首页或者相应路由加载时还是要加载这样一个几百 kb 的文件,还是不利于性能优化的。这时,我们可以用 CDN 的方式引入这样插件或者 UI 组件库。
- 在
index.html
引入相应 cdn 链接
<head> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/styles/iview.css" /></head><body> <div id="app"></div> <script src="https://cdn.bootcss.com/vue/2.6.8/vue.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/iview.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/xlsx.mini.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/cpexcel.min.js"></script></body>
-
vue.config.js
配置externals
configureWebpack: (config) => { config.externals = { vue: 'Vue', xlsx: 'XLSX', iview: 'iView', iView: 'ViewUI', }}
- 删除之前的引入方式并卸载相应 npm 依赖包
npm uninstall vue iview echarts xlsx --save
此时我们在来看一下打包后的情况
![image](https://upload-images.jianshu.io/upload_images/6334858-21d7230400eefb3b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
打包出来的 js 文件
![image](https://upload-images.jianshu.io/upload_images/6334858-3a1a85942caabe09.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
well done ! 这时基本没有打包出大文件了,首页加载需要的 vendor.js 也只有几十 kb,而且我们还可以进一步优化,就是把 vue 全家桶的一些模块再通过 cdn 的方法引入,比如 vue-router,vuex,axios 等。这时页面特别是首页加载的性能就得到大大地优化了。