跳至主要內容

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

星火燎原@vxhly大约 4 分钟vuejsVue.jsNode.js

前言

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

好景不长

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

首先是文件大小

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

vue-performance
vue-performance

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

加载速度

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

vue-performance
vue-performance

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

CDN

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

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
vue-performance

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

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

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

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

Gzip

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

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

http {
    gzip on;
    gzip_static on;
    gzip_min_length 1024;
    gzip_buffers 4 16 k;
    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
                    })
            }
        }
打赏
给作者赏一杯咖啡吧
您的支持将是我继续更新下去的动力
微信微信
支付宝支付宝