优化方向:
- 路由懒加载
- 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 一次,用作后续优化的参照。如下图:
将项目部署在本地 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。
为项目添加路由懒加载功能,如下图:
webpackPrefetch
和 webpackPreload
同webpackChunkName
是一个特殊的注释语法,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 资源可以有效减少我们自己服务器的带宽压力。
以本文的演示项目为例优化步骤如下:
将 vue、vuex、vue-router、mint-ui 和 axios 这五个库全部改为用 CDN 导入。在 HtmlWebpackPlugin 中配置 CDN 链接,然后在 index.html 中用循环语法插入 js 和 css 的 CDN 链接。
HtmlWebpackPlugin 配置
index.html
html 中使用 EJS 语法循环插入 CDN 链接,EJS 一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。更多信息可查看官方文档。
在 build/webpack.base.conf.js 中添加
externals
不把从 CDN 导入的库打包到 bundle 中,如下:1
2
3
4
5
6
7externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios':'axios',
'mint-ui': 'MINT',
}键名是 import 的 npm 包名,键值是该库暴露的全局变量。
externals
详细信息可查看官方文档 Externals。卸载依赖的 npm 包,
npm uninstall axios mint-ui vue vue-router vuex
删除 main.js 中 mint-ui 相关的代码
重新构建一次,优化前后文件对比如下:
优化后:
优化前:
从以上对比我们可以看到以下两点:
- app.css 从 33.5KB 降到了只有 432bytes,app.css 包含的是压缩后的样式文件,由于我们把 mint-ui 的样式直接通过 CDN 链接引入,所以现在 app.css 中只包含少量的项目中的 css。
- 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 | productionGzip: true, |
重新 build,可以发现多了一些 .gz 的文件
总结
以上,总结了我在实践中发现的优化效果较明显的三种方案。优化是无止境的,除了以上的方案以外还有很多地方值得研究。比如,css、js 压缩,小图片转 base64,第三方 UI 组件按需引入,利用 prerender-spa-plugin
插件实现预渲染等等。