背景
参与到团队一个三年陈项目,业务庞大繁杂。但是代码基本只按照一级路由拆分,很多模块在压缩前甚至超过 2M。导致一个微小的改动都会重新生成新的 bundle 文件,用户在加载新资源会等待很长时间,对时间和网络资源造成严重浪费。
任务
- 实现按需加载,将代码按照模块合理分割
- 将公共代码和第三方包,独立打包,减少重复打包的情况
- 大文件拆分
- 减少第三方包大小
- 使用 bundle 文件缓存
优化前我们可以通过 webpack-plugin-analyze 分析打包后的文件,通过可视化页面我们发现:
- 最大的文件是 js/chronic.[hash].js 文件,里面包含了一级路由下的所有页面,而且业务模块和第三方 node_modules 都被打包在一起
- 一些第三方模块,xlsx.js 被多次打包在不同的 chunk 中
通过控制台我们也可以得到一些打包后的信息,会告诉我们哪些文件过大需要优化:
行动
按需加载,将页面一个 chunk
// router.js
// 通过 webpackChunkName 指定 chunk 的名称
const NotFind = () => import(/* webpackChunkName: "not-found" */ 'Components/404');
公共资源打包
避免多次加载的资源重复的打包到 chunk 中
// vue.config.js
// 配置 webpack
module.exports = {
...
chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all', cacheGroups: {
// 第三方公共包 vendor: { name: 'vendor', test: /[\\/]node_modules[\\/]/, priority: 10, // 打包权重,数值越大权重越高 chunks: 'initial', // 只打包初始时的依赖 }, common: { name: 'common', minSize: 30, minChunks: 2, //模块被引用2次以上的才抽离 priority: 0, reuseExistingChunk: true, }, }
})
}
}
拆分大文件
拆分一些不常用的第三方包,减少 vender 体积
// vue.config.js
module.exports = {
...
chainWebpack: config => {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
echarts: { name: 'vendor-echarts', priority: 15, // 权重要大于 vendor 和 common test: /[\\/]node_modules[\\/]echarts[\\/]/, }, xlsx: { name: 'vendor-xlsx', priority: 15, test: /[\\/]node_modules[\\/]xlsx[\\/]/, },
... }
})
}
}
减少第三方包体积
以 moment.js 为例,其中包含了语言包,而实际情况我们只会使用 1~2种
// webpack plugin
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // 忽略 moment.js的所有本地文件
// src/App.vue
// 引入中文依赖import 'moment/locale/zh-cn';
使用 chunkhash
使用 hash 的话,每次都会生成新的 hash,导致即使不做修改,两次生成的文件 hash 也不同。修改后只有在 chunk hash 改变时才会生成新的 chunk 文件
// vue.config.js
const hashType = process.env.NODE_ENV === 'production' ? 'chunkHash' : 'hash';
module.exports = {
...
chainWebpack: config => {
config.output.filename(`[name].[${hashType}].js`);
}
}
结果
打包后文件文件体积更小,粒度更细,能够满足按需加载;公共文件被提取出来了,大文件也根据使用频率拆分为更小的包