zoukankan      html  css  js  c++  java
  • 优化一下基于 vue-cli4 的项目

    公司的一个管理后台项目是基于 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 项目里不需要我们手动添加模式。个人觉得优化方案选择合适的就好,没必要全上,可以把更多的心思花在业务代码的优化上。

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

  • 相关阅读:
    思维探索者:理解了才是真正的学习 理解会带来巨大的好处
    思维探索者:让记忆与学习更有效率 别在别人的思考中得意着
    思维探索者:从问题到答案的思维过程 像侦探一样思考
    android-HttpClient上传信息(包括图片)到服务端
    java web每天定时执行任务
    基础总结篇之一:Activity生命周期
    Spring面试题一
    J2EE用监听器实现同一用户只能有一个在线
    notepad++快捷键大全
    python for循环巧妙运用(迭代、列表生成式)
  • 原文地址:https://www.cnblogs.com/wjaaron/p/13370481.html
Copyright © 2011-2022 走看看