webpack 打包优化

1,608 阅读2分钟

背景

参与到团队一个三年陈项目,业务庞大繁杂。但是代码基本只按照一级路由拆分,很多模块在压缩前甚至超过 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`);
    }
}

结果

打包后文件文件体积更小,粒度更细,能够满足按需加载;公共文件被提取出来了,大文件也根据使用频率拆分为更小的包