1

记一次Vue-CLI生产项目的打包优化

前言

此次是接手一个老项目(老项目大家都懂的,想哭😭);项目里面的依赖繁杂,而且引用也不太规范。就先吐槽吐槽项目,首先:项目里引用了三个UI组件库,Elementview-design、还有一个xxx-admin的(不要问为什么);其次:样式里LessSass同时使用,混乱不堪;最后:一些非生产依赖通通安装 dependencies 下。吐槽先放放,下面就开始说说我对项目的优化。

项目初始体积和打包速度

第二张图是初次打包时间,3分多钟;第三张图是再次打包,1分20秒的样子,因为Vue CLI 本身对优化的已经做了,所以我们只需针对项目优化即可,因而后面打包时间相对第一次会减少一些。

项目打包体积

项目打包时间

不做优化再次打包时间

查看分析打包可以使用CLI自带命令:"build": "vue-cli-service build --report",打包后 dist 目录会生成 report.html 文件,用来分析各文件的大小,或者安装插件 webpack-bundle-analyzer

# 安装插件
npm install webpack-bundle-analyzer -D

# 测量各个插件和 loader 所花费的时间插件
npm i speed-measure-webpack-plugin -D

# vue.config.js 中引用
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

module.exports = {
  configureWebpack: smp.wrap({
      plugins: [
         new BundleAnalyzerPlugin()
      ]
  })
}

组件库的按需引用

目前高版本的组件库基本默认支持基于 ES modulestree shaking,直接引入组件就会有按需加载的效果。而项目还是在 Vue2 的版本,针对组件库的按需加载,一般是借助依赖 babel-plugin-import,将 .babel.config.js 修改为:

# Element 举例,各UI组件库都有对应文档,查看文档按照文档中配置即可
{
    "plugins": [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
    ]
}

而我的项目因为历史原因,组件库这块不打算动它了,动了的痛想必各位小伙伴都能体会其中的痛😖。

减少 moment 插件体积

针对时间处理,项目引用的是 moment,打包后有很多没有用到的语言包,而项目目前并没有多语言需求,所以可以删除多余语言包

# 安装依赖
npm install moment-locales-webpack-plugin -D

const MomentLocalesPlugin = require('moment-locales-webpack-plugin');

module.exports = {
  configureWebpack: {
     plugins: [
       new MomentLocalesPlugin({localesToKeep: ['zh-cn']})
     ]
  }
}

前后对比:

打包前
 title=

打包后
 title=

从图片可以看出,前后直接减少有200KB

目前的打包时间为:

目前打包时间

多线程打包

thread-loader 实现多线程打包,它把任务分解给多个子进程去并发的执行,来提升打包速度。

Vue-CLI 已经内置thread-loaderthread-loader 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启。

# 此配置不建议一开始就使用,适用于大项目里比较耗时的loader
module.exports = {
  parallel: true,
}

经本项目多次打包构建测试,开启后平均时间为 50s 左右,偶发性一次在 33s,不开启平均 45s 左右,因此视情况而定。

移除console

webpack v5 开箱即带有最新版本的 terser-webpack-plugin。如果你使用的是 webpack v5 或更高版本,同时希望自定义配置,那么仍需要安装 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本
# 安装依赖
npm install terser-webpack-plugin --save-dev

# 使用
const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  configureWebpack: {
      optimization: {
        minimize: true,
        minimizer: [
          new TerserPlugin({
            terserOptions: {
              format: {
                comments: false // 删除注释
              },
              compress: {
                warnings: false,
                drop_console: true, // 删除console
                drop_debugger: true // 删除debugger
              }
            },
            extractComments: false // 删除注释
          })
        ]
      }
  }
}

Gzip压缩

# 安装依赖
npm install compression-webpack-plugin -D

module.exports = {
  chainWebpack: config => {
    // 开启js、css压缩,生成gz压缩文件
    if (VUE_APP_ENV_PROD) {
      config
        .plugin('CompressionWebpackPlugin')
        .use(require('compression-webpack-plugin'), [
          {
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: new RegExp('\\.(js|json|css)$'),
            threshold: 10240, // 资源文件大于10240B=10kB时会被压缩
            minRatio: 0.8 // 最小压缩比达到0.8时才会被压缩
            // deleteOriginalAssets: true // 删除原文件
          }
        ])
        .end()
    }
  },
}

生产配置CDN

关于配置CDN,个人建议如果公司有自己的CDN库,可以配置,如果没有而是引用第三方的,这个得酌情考虑了,因为我自己有遇见引用三方CDN,出现挂了的情况,导致线上报错了。

第三方CDN地址:

  1. vue.config.js 中配置:
module.exports = {
  configureWebpack: {
    externals: {
      vue: 'Vue',
     'vue-router': 'VueRouter',
      vuex: 'Vuex',
      axios: 'axios',
      echarts: 'echarts',
    }
}
  1. index.html 中使用 CDN 引入依赖
<body>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-router@3.1.6/dist/vue-router.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.min.js"></script>
</body>
  1. 以上手动配置不太方便,可借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入
// 生产配置
const cdn_production = {
  js: [
    '//cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.min.js',
    '//cdn.jsdelivr.net/npm/vue-router@3.1.6/dist/vue-router.min.js',
    '//cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js',
    '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
    '//cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.min.js'
  ]
};
​
module.exports = {
  configureWebpack: {
    externals: {
      vue: 'Vue',
     'vue-router': 'VueRouter',
      vuex: 'Vuex',
      axios: 'axios',
      echarts: 'echarts',
    },
  },
  chainWebpack: config => {
       if (VUE_APP_ENV_PROD) {
          config.plugin('html').tap(args => {
          args[0].cdn = cdn_production
          return args
        })
       }

  }
};

# index.html中添加:

<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
  <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>

配置CDN测试后,时间确实缩减明显:

配置CDN构建时间

图片优化

关于图片优化,有先推荐大家去小熊猫网址压缩,效果非常不错。地址:https://tinypng.com/。而在项目中,图片压缩可以使用 image-webpack-loader,但是有可能安装失败,将源重新设置一下,如更换淘宝的。

# 安装
npm install image-webpack-loader --save-dev

# 配置
chainWebpack: config => {
   config.module.rule('images')
        .test(/\.(gif|png|jpe?g|svg)$/i)
        .use('image-webpack-loader')
        .loader('image-webpack-loader')
        .options({
          mozjpeg: { progressive: true, quality: 50 }, // 压缩JPEG图像
          optipng: { enabled: true }, // 压缩PNG图像
          pngquant: { quality: [0.5, 0.65], speed: 4 }, // 压缩PNG图像
          gifsicle: { interlaced: false } // 压缩GIF图像
        })
        .end()
}

文件资源有减少,但是打包时间也确实增加了。前后对比:

使用前(21M 还是很多图片经过熊猫网站压缩后的结果)
使用插件前

使用后
使用插件后

去除生产环境sourceMap

sourceMap资源映射文件,存的是打包前后的代码位置,方便开发使用,这个占用相当一部分空间。

map文件的作用在于:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错,有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。

module.exports = {
    productionSourceMap: false
}

DLL 动态链接库

但在 vue-cli 引入 webpack4 之后,移除了该包,"因为 Webpack 4 的打包性能足够好的,dll 没有在 Vue ClI 里继续维护的必要了。"

dll option will be removed. Webpack 4 should provide good enough perf and the cost of maintaining DLL mode inside Vue CLI is no longer justified.

配置可参考webpack地址:https://webpack.docschina.org/plugins/dll-plugin#root

总结

从打包分析图看,大部分在node_modules地图资源的json中,业务代码所占比例并不是非常大,而且可能也跟项目写的不太规范有关。而针对图片资源的优化,我觉得性价比是最高的,在经过自己压缩图片物理大小都有20M,经压缩后才有6M,图片的压缩率70%;但是相对的时间确实也上去了,但是我觉得我是可以接受的。内容当然还是有其他一些细节是可以优化的,但是这个可能是需要后期边做边优化。

经过上面的针对性的优化,总结一下表格信息:

对比项优化前优化后优化率说明
体积15.27M12.46M近20%没想象中高
时间(未压缩图片)1分34秒53秒44%平均测试
时间(压缩图片)1分34秒3分42秒--平均2分47秒在图片压缩loader上,单看时间打包远远偏高了,无任何优化,但针对项目资源访问速度,那是远比之前提高了

这些也都是大家项目中可以使用的一些方式,针对项目合理使用可以有效地提升项目的性能。


提莫找蘑菇
1.9k 声望39 粉丝

不要给我说什么:react/angular/Typescript/vue/es6/es7/babel/webpack....老夫就用Jquery!0.0