Vue 项目性能优化(首屏优化)

随着 Vue 项目越做越大,依赖的第三方 npm 包越来越多,构建之后的文件也会越来越大,尤其是 vendor.js,甚至会达到 2M 左右, 为了解决这个问题,需要做一些探索,在几乎不需要改动业务代码的情况下,找到了几种有明显效果的优化方案 —— CDN + Gzip

好景不长

先来看看没有任何优化的项目需要优化些什么

首先是文件大小

这是一个通过 Vue-cli 3.0 构建的 demo 项目,先执行一下 npm run build,当然也可以通过 vue ui 的 GUI 界面进行编译

vue-performance

可以看到这个默认项目的各个 JS 文件还可以,不算是比较大的

加载速度

禁用浏览器缓存,网速限定为 Fast 3G 下的 Network 图(运行在本地的nginx服务器上)

vue-performance

嗯~ ,故意调了个比较慢的网络环境,可以看到这边的加载速度真的慢到极致了

CDN

首先是 CDN 优化,把比较经常使用的依赖,像 vue,vue-router,vuex,element-ui,axios,loadsh 直接使用 CDN 来分发,这边选用的 CDN 服务是 bootcdn

vue.config.js

const externals = {
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios',
  'element-ui': 'ELEMENT',
  '_': 'lodash'
}

const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/theme-chalk/index.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/theme-chalk/index.css'
    ],
    js: [
      'https://cdn.bootcss.com/vue/2.5.17/vue.min.js',
      'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
      'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
      'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
      'https://cdn.bootcss.com/lodash.js/4.17.11/lodash.min.js',
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/index.js'
    ]
  }
}

module.exports = {
  productionSourceMap: false,
  configureWebpack: config => {
    const myConfig = {}
    if (process.env.NODE_ENV === 'production') {
      // 1\. 生产环境 npm 包转 CDN
      myConfig.externals = externals
    }
    if (process.env.NODE_ENV === 'development') {
      /**
       * 关闭host check,方便使用ngrok之类的内网转发工具
       */
      myConfig.devServer = {
        disableHostCheck: true
      }
    }
    return myConfig
  },
  chainWebpack: config => {
    /**
     * 删除懒加载模块的prefetch,降低带宽压力
     * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
     * 而且预渲染时生成的prefetch标签是modern版本的,低版本浏览器是不需要的
     */
    config.plugins
      .delete('prefetch')
    /**
     * 添加CDN参数到htmlWebpackPlugin配置中
     */
    config
      .plugin('html')
      .tap(args => {
        if (process.env.NODE_ENV === 'production') {
          args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
          args[0].cdn = cdn.dev
        }
        return args
      })
  }
}

index.html

<!DOCTYPE html>
<html lang="en">

<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>demo</title>
  <!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
  <% } %>
</head>

<body>
  <noscript>
    <strong>We're sorry but demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
  <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>
  <!-- built files will be auto injected -->
</body>

</html>

优化对比

再来看看使用了 CDN 之后的编译大小

vue-performance

天哪!!! chunk-vendors 这个文件小了不是一点点

chunk-vendors 该文件打包了 vue,vuex,vue-router 等依赖资源,这也是这个文件之所以大的原因

因为是优化产品模式下的代码,这里就构建一个 Nginx 服务器来查看加载的情况

构建的话,自行百度,比较懒的推荐 upupw ,当然闲的无聊的话可以写一个 express 当作本地服务器

vue-performance

果然加载的速度变快了许多

Gzip

使用 Gzip 两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。

开启 gzip 的方式主要是通过修改服务器配置,这里贴出 nginx 的配置方式

http {
  gzip on;
  gzip_static on;
  gzip_min_length 1024;
  gzip_buffers 4 16k;
  gzip_comp_level 2;
  gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
  gzip_vary off;
  gzip_disable "MSIE [1-6]\.";
}

当然只有 nginx 这边开启了 gzip 是不够的,webpack 工具提供了 productionGzip 来开启 gzip

在此之前还需安装一个依赖项

npm i -D compression-webpack-plugin

补全 vue.config.js

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

const externals = {
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios',
  'element-ui': 'ELEMENT',
  '_': 'lodash'
}

const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/theme-chalk/index.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/theme-chalk/index.css'
    ],
    js: [
      'https://cdn.bootcss.com/vue/2.5.17/vue.min.js',
      'https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js',
      'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
      'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
      'https://cdn.bootcss.com/lodash.js/4.17.11/lodash.min.js',
      'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/index.js'
    ]
  }
}

// 是否使用gzip
const productionGzip = true
// 需要gzip压缩的文件后缀
const productionGzipExtensions = ['js', 'css']

module.exports = {
  productionSourceMap: false,
  configureWebpack: config => {
    const myConfig = {}
    if (process.env.NODE_ENV === 'production') {
      // 1\. 生产环境 npm 包转 CDN
      myConfig.externals = externals
      myConfig.plugins = []

      // 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
      productionGzip && myConfig.plugins.push(
        new CompressionWebpackPlugin({
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 8192,
          minRatio: 0.8
        })
      )
    }
    if (process.env.NODE_ENV === 'development') {
      /**
       * 关闭host check,方便使用ngrok之类的内网转发工具
       */
      myConfig.devServer = {
        disableHostCheck: true
      }
    }
    return myConfig
  },
  chainWebpack: config => {
    /**
     * 删除懒加载模块的prefetch,降低带宽压力
     * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
     * 而且预渲染时生成的prefetch标签是modern版本的,低版本浏览器是不需要的
     */
    config.plugins
      .delete('prefetch')
    /**
     * 添加CDN参数到htmlWebpackPlugin配置中
     */
    config
      .plugin('html')
      .tap(args => {
        if (process.env.NODE_ENV === 'production') {
          args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
          args[0].cdn = cdn.dev
        }
        return args
      })
  }
}
亲!!! 听说给作者打赏一杯咖啡钱,会给自己带来好运哦!