vue-cli 项目可以做的优化

公司的一个管理后台项目是基于 vue-cli3 搭建的,这两天我将它升级到了 vue-cli4,顺手也做了一些优化,主要是从 webpack 方面入手,优化一下生产环境的代码。

这里简单提一下怎么升级脚手架版本的,首先将你电脑中的脚手架版本升级到 4,直接重新安装就好,然后在你的项目中执行 vue upgrade 这个命令,根据提示一步一步地去升级即可。这里就不多描述细节了,网上能找到很多文章。

接下来主要聊聊 vue-cli 项目中可以配置的一些优化方案,下面提到的方案也并不是我都用上的,还是需要根据真实项目来决定用或不用。

删除 console.log

在开发时,我还是喜欢用 console.log 来调试,但是部署到线上时,最好还是删掉这部分代码。别看代码不多,当项目到了一定规模时,还是占用不少体积的。脚手架内部使用的是 TerserWebpackPlugin 这个插件来压缩js代码的,它自带一个删除 console.log 的功能,所以直接配置即可,在 configureWebpack 中配置,如下:

{
    // ...
    
    configureWebpack: config => {
        // 生产环境下生效
        if (process.env.NODE_ENV === 'production') {
          // 配置删除 console.log
          config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
        }
    }

    // ...
}

 

启用 gzip 压缩

使用 gzip 压缩代码,效果显著。gzip 是需要服务端配置的,当开启时会压缩我们的线上代码,但是如果我们前端已经使用 gzip 压缩过,那么服务端就会直接使用已经压缩好的代码,就不需要花时间再去压缩了。前端启用 gzip 压缩,使用到的插件是 

CompressionWebpackPlugin ,在 configureWebpack 中配置如下:
// 需要先安装这个插件
// npm install compression-webpack-plugin

const CompressionWebpackPlugin = require('compression-webpack-plugin')

{
  // ...

  configureWebpack: config => {
    // 生产环境下生效
    if (process.env.NODE_ENV === 'production') {
      // 配置 gzip 压缩
      config.plugins.push(
        new CompressionWebpackPlugin({
          test: /\.js$|\.html$|\.css$/,
          threshold: 4096 // 超过4kb压缩
        })
      )
    }
  }

  // ...
}

 

当配置好 gzip 后,再去打包,会发现打包文件中有 .gz 结尾的文件,说明配置 gzip 压缩成功了:

 

当然你可能还需要跟后端沟通一下,得让服务端开启这个功能。

 

代码分割

对于 vue 项目,如果你留心过打包文件,会发现一个叫 chunk-vendors 的 js 文件,这个文件包含了业务代码和一些引入的第三方库代码,所以一般体积比较大,浏览器加载时也耗时更多。

 

其实我们可以将这部分代码做一个分割,分成多个文件,这样浏览器加载时会并行加载,而且对于不变的代码,会直接从缓存中读取,速度得到提升。

webpack 提供了一个配置可以实现这样的功能,从 webpack4 开始,可以在 optimization.splitChunks 中配置代码分割功能,可以参考文档 splitChunksPlugin 。直接看文档可能有些懵,建议多搜些此类文章看看,我这里直接给出在 vue.config.js 中配置 splitChunks 的做法,我是在 chainWebpack 中配置的:

{
  // ...

  chainWebpack: config => {
    config
      .when(process.env.NODE_ENV === 'production', // 配置生产环境生效
        config => {
          config.optimization.splitChunks({
            chunks: 'all', // 将对什么类型的代码进行分割,三种值:all: 全部 | async: 异步,按需加载的代码 | initial: 入口代码块
            cacheGroups: { // 缓存组
              // 定义 libs 缓存组,分割从 node_modules 中引入的代码
              libs: {
                name: 'chunk-libs', // 分割成的文件名
                test: /[\\/]node_modules[\\/]/, // 匹配 node_modules 中模块
                priority: 10, // 优先级,当模块同时命中多个缓存组的规则时,分配到优先级高的缓存组
                chunks: 'initial' // 这里覆盖上面的 chunks: 'all',仅打包最初依赖的第三方库
              },
              // 项目使用 iview 组件开发的,定义 iviewUI 缓存组,用于分割 iview 代码
              iviewUI: {
                name: 'chunk-iviewUI',
                priority: 20, // 优先级 20,命中 iview 代码时,优先分割到此组里
                test: /[\\/]node_modules[\\/]_?iview(.*)/ // 匹配 iview 代码
              }
            }
          })
        }
      )
  }

  // ...
}

 

目前我只做了简单的分割,对我的项目而言已经足够了,splitChunks 的配置项有很多,建议多看看此方面的资料。打包后可以看到代码已被分割:

 

 

配置 CDN

老实说,我不用这个功能的,线上使用 cdn 总让我有一种不安全感,除非公司有自己的 cdn 库,不过这确实也是一种优化方案,效果也还不错。它的配置也很简单,在 externals 中配置,例子:

{
  // ...

  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      // 配置 cdn,这里将 vue,vue-router 和 axios 三个包配置成 cdn 引入
      // 其中 Vue,VueRouter 等名称是该库暴露在全局中的变量名
      config.externals = {
        vue: 'Vue',
        'vue-router': 'VueRouter',
        axios: 'axios'
      }
    }
  }

  // ...
}

然后在 public/index.html 模板文件中引入 cdn 地址:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title></title>
    <!-- 引入 cdn 地址 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.10/vue.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.18.0/axios.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

我这里使用的是 bootcdn 的地址,需要注意版本问题。也可以借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入。

使用 cdn 引入的方式虽然能极大改善网页加载速度,但我还是不会用这个功能,项目还不需要非得这样的优化,也怕 cdn 不稳定。觉得需要的还是可以上的。

 

图片压缩

图片压缩会影响图片质量,这个根据个人需求来选择。管理后台项目一般图片不多,所以我自己没有用上。试验一下也未尝不可,需要安装 image-webpack-loader ,配置如下:

{
  // ...

  chainWebpack:config => {
    // 配置图片压缩
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
      .end()
  }

  // ...
}

可以看一下效果,配置前的某图片:

配置图片压缩后:

效果还是显著的,对图片质量要求不高的可以配置上。

 

IgnorePlugin

webpack 中的 IgnorePlugin 插件,可以在打包第三方包时忽略一些文件内容,有效减小打包文件体积。一个典型的案例就是忽略 moment 插件的语言包,moment 自带有很多语言包,即使你不用这些包,也会被一起打包了,这时候就可以使用 ignorePlugin 来忽略打包这部分文件,配置插件:

{
  // ...

  plugins:[
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) // 忽略打包语言包
  ]

  // ...
}

在使用时,只需要引入自己需要的那个语言包即可:

import moment from 'moment'
// 引入中文
import 'moment/locale/zh-cn'
// 设置中文
moment.locale('zh-cn');
let momentStr = moment().subtract(10, 'days').calendar();

这里只是例举 moment 这个例子,关于时间格式化,我还是喜欢自己去封装,或者可以使用 dayjs 这个库,更轻量化。

 

DllPlugin

这个适用于开发环境,每次构建都需要打包引入的第三方模块,如果不打包这些模块,那么构建速度将大大提升,这正是 DllPlugin 要做到的事。

说的明白点就是在构建项目之前,先把一些第三方包打包好,然后项目构建时直接引用这些打包好的第三方包,提升项目构建速度。

 

需要使用到两个 webpack 内置的插件,分别是 DllPlugin 和 DllReferencePlugin。使用 DllPlugin 插件打包出 dll 文件,使用 DllReferencePlugin 插件使用 dll 文件。

 

我这里以打包 lodash 这个第三方模块为例。首先要编写一个 webpack 配置文件用以打包 lodash 模块:

// webpack.dll.js

const webpack = require('webpack')
const path = require('path')
module.exports = {
    mode:'development',
    entry:{
        lodash:['lodash']
    },
    output:{
        // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
        // 在这里 name 指的就是 lodash
        filename:'[name].dll.js',
        // 输出的文件放到这个目录下
        path:path.join(__dirname,'public'),
        // 存放动态链接库的全局变量名称。这里就是 _dll_lodash
        library:'_dll_[name]'
    },
    plugins:[
        new webpack.DllPlugin({
            // 该字段的值需要和 output.library 中保持一致
            // 该字段的值也就是输出的 manifest.json 文件中的 name 字段的值
            name:'_dll_[name]',
            // 描述动态链接库的 manifest.json 文件输出时的文件名称
            path:path.join(__dirname,'public/[name].manifest.json')
        })
    ]
}

然后可以在 package.json 的 scripts 中写入一个命令:

scripts:{
    // ...
    "dll": "webpack --config webpack.dll.js"
}

此时执行 npm run dll 即可打包出 lodash 的 dll 文件。

 

接下来还有两个重要步骤,一个是要在模板文件中手动引入刚打包出来的文件:

<body>
    <script src="../public//lodash.dll.js"></script>
</body>

在 vue.config.js 中使用 DllReferencePlugin 插件来引用映射文件:

{
  // ...

  configureWebpack: config => {
    if (process.env.NODE_ENV === 'development') {
      config.plugins.push(new webpack.DllReferencePlugin({
        manifest: require('./public/vendor/vendor.manifest.json')
      }))
    }
  }

  // ...
}

这样一套配置下来后,项目构建时就不会每次都打包 lodash 这个包了。

 

结语

优化方案绝不止我上面提到的几种,比如路由懒加载也算是一种优化,vue 项目中建议都用上路由懒加载,再比如考虑给 babel 加上缓存优化,甚至添加 mode: production 都能达到优化的目的,webpack 在生产模式下会自动开启一些优化(Tree Shaking,Scope Histing 等),当然 vue-cli 项目里不需要我们手动添加模式。个人觉得优化方案选择合适的就好,没必要全上,可以把更多的心思花在业务代码的优化上。

有错处之处还望指出,谢谢。

posted @ 2020-07-24 10:20  黑色瓶子  阅读(3880)  评论(0编辑  收藏  举报