优化方向:

  • 路由懒加载
  • CDN 优化
  • Gzip 优化

Vue CLI 2 和 Vue CLI 3 构建的项目均适用以下优化方案,本文主要基于 Vue CLI 2 进行讲解。

准备工作

通过 Vue CLI 2 和 webpack 模板构建一个基础的项目,在 vue、vuex、vue-router 的基础上添加 mint-ui 和 axios 两个常用的库。创建两个路由 Home 和 About。创建完成后 build 一次,用作后续优化的参照。如下图:

original

将项目部署在本地 Apache 服务器,禁用浏览器缓存,网速限定为 Slow 3G 首页加载情况如下图:

如上图,未优化的情况需要12s左右才能加载完成。

路由懒加载

构建应用过程中生成的 app.js 主要包含项目中的业务代码,随着项目越来越复杂 app.js 文件会变得非常大,严重影响首屏加载速度,显然在首屏加载的时候就把所有的 JavaScript 加载进来也是不合理的。

我们可以结合 Vue 的异步组件和 Webpack 的代码分割功能,实现路由懒加载功能。把不同路由对应的组件分割成不同的代码块,只有当路由被访问的时候才加载对应的组件,有效减少首屏加载时加载的文件大小。

我们可以通过如下格式定义能够被 Webpack 自动进行代码分割的异步组件:

1
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')

webpackChunkName 是一个特殊的注释语法(需要 Webpack > 2.4),为分割后的代码块提供了一个 chunk name,构建应用后 Foo.vue 组件对应的代码块为 group-foo.js。

为项目添加路由懒加载功能,如下图:

webpackPrefetchwebpackPreloadwebpackChunkName 是一个特殊的注释语法,prefetch 代表资源在将来的导航中会被使用,preload 代表资源要在当前导航中使用,主要区别如下:

  • preload chunk 与父 chunk 并行加载,prefetch chunk 在父 chunk 加载完成后加载。
  • preload chunk 具有 medium 优先级可立即下载,prefetch chunk 在浏览器空闲时才下载。
  • preload chunk 应被父 chunk 立即请求,prefetch chunk 可以在将来任何时候使用。

想了解更多更详细的 Vue 异步组件和 Webpack 代码分割功能可参考官方文档 Vue 异步组件Webpcak Code Spliting

重新 build 一次,如下图:

可以看到 app.js 从12.2KB 变成了 10.3KB,同时多了两个 js 文件 hello-world.js 和 about.js。重新部署一遍,首屏加载情况如下图:

从上图可以看到,首屏加载的时候并没有加载 About.vue 组件对应的 1.js 文件,只有在我们访问 /about 路由的时候才会加载。

由于这个演示项目只是一个空项目 About.vue 只有 450bytes 大小,所以对于首屏加载的速度并没有什么影响,但是在具体的项目中,首页加载时对一些不必要的东西延迟加载,对于首页加载速度来说是很有帮助的。

CDN 优化

将项目中依赖的第三方库使用 CDN 链接引入,不仅是首屏加载速度上会有较大的提升,且在高并发时大部分的静态资源都请求的第三方的 CDN 资源可以有效减少我们自己服务器的带宽压力。

以本文的演示项目为例优化步骤如下:

  1. 将 vue、vuex、vue-router、mint-ui 和 axios 这五个库全部改为用 CDN 导入。在 HtmlWebpackPlugin 中配置 CDN 链接,然后在 index.html 中用循环语法插入 js 和 css 的 CDN 链接。

    HtmlWebpackPlugin 配置

    index.html

    html 中使用 EJS 语法循环插入 CDN 链接,EJS 一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。更多信息可查看官方文档

  1. 在 build/webpack.base.conf.js 中添加 externals 不把从 CDN 导入的库打包到 bundle 中,如下:

    1
    2
    3
    4
    5
    6
    7
    externals: {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'axios':'axios',
    'mint-ui': 'MINT',
    }

    键名是 import 的 npm 包名,键值是该库暴露的全局变量。

    externals 详细信息可查看官方文档 Externals

  2. 卸载依赖的 npm 包,npm uninstall axios mint-ui vue vue-router vuex

  3. 删除 main.js 中 mint-ui 相关的代码

重新构建一次,优化前后文件对比如下:

优化后:

优化前:

从以上对比我们可以看到以下两点:

  1. app.css 从 33.5KB 降到了只有 432bytes,app.css 包含的是压缩后的样式文件,由于我们把 mint-ui 的样式直接通过 CDN 链接引入,所以现在 app.css 中只包含少量的项目中的 css。
  2. vendor.js 从 244KB 降到了只有 828bytes,体积大幅度减少。vendor.js 主要包含的是项目中依赖的第三方库源码,所以在把 5 个依赖库全部转为 CDN 链接后,体积得到了大幅度的减少,里面已经没有了第三方库了。

禁用浏览器缓存,网速限制为 Slow 3G,优化后的首页加载情况如下图:

与优化之前对比,加载时间从12秒左右减少到了9秒左右,快了25%左右。

Gzip 优化

开启 Gzip 的方式主要是通过修改服务器配置,本文只讨论 Gzip 前端能做的事情。

使用 Gzip 两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。服务器在返回 js 文件的时候,会先判断是否有 .gz 后缀的文件,如果有就直接返回,否则在压缩后返给浏览器,所以我们在 build 的时候做好压缩也就不用占用服务器的 CPU 资源去压缩。

首先,安装 compression-webpack-plugin

1
npm install --save-dev compression-webpack-plugin

然后,把 config/index.js 中 productionGzip 设置为 true

1
2
productionGzip: true,
productionGzipExtensions: ['js', 'css']

重新 build,可以发现多了一些 .gz 的文件

总结

以上,总结了我在实践中发现的优化效果较明显的三种方案。优化是无止境的,除了以上的方案以外还有很多地方值得研究。比如,css、js 压缩,小图片转 base64,第三方 UI 组件按需引入,利用 prerender-spa-plugin 插件实现预渲染等等。