10 秒 到 2秒 超简单的Vue项目首屏优化实践
文章目录
摘要
性能优化是一个比较大的知识点,包含但不限于性能优化的工具,技术手段和流程,本文主要从压缩文件体积,减少请求次数方面入手,优化Vue项目的首屏加载时间,实现首屏加载10秒到2秒的提升。主要技术栈如下:Vue cli3
Element UI
首屏优化
Vue 是典型的单页应用,首次加载耗时多,因此优化Vue项目首屏加载对于提升用户体验非常重要。下图展示了一个糟糕的首屏加载案列,白屏时间长达10余秒,简直让人无法忍受!
如何优化
方法一: 压缩文件体积
- 压缩文件体积操作简单粗暴,效果却非常明显。常见的压缩文件体积的方法如下:
- 压缩图片体积,设置合适的图片尺寸
- 多图页面采用雪碧图(尤其是小图片)
- 开启GZIP压缩
- 关闭源码视图,清除
console.log
日志
上述方案可根据具体情况考虑,比如我手下的项目主要是Vue+Element UI搭建的后台管理系统,并未涉及图片,因此只需采取方案3和方案4即可。
开启GZIP压缩
- Vue项目前端配置GZIP
- 安装
CompressionPlugin
npm i compression-webpack-plugin -D
- 在
vue.config.js
中进行配置
vue cli3 将webpack配置和vue配置都集中在
vue.config.js
中管理,这一点需要注意和vue cli2 的区别.如果没有vue.config.js
文件可自行在项目根目录下创建\
const CompressionPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
/** 开发环境 */
const DEV = process.env.NODE_ENV !== 'production';
module.exports = {
/* vue.config.js支持webpack-chain写法 */
chainWebpack: config =>{
/* *******************************************
*
* 开启GZIP压缩
* 压缩前:4.4MB
* 压缩后:1.7MB
* @Author: mingyong.g
* @Date: 2020-09-02 19:55:13
*
********************************************/
if(!DEV){
config.plugin('compressionPlugin')
.use( new CompressionPlugin({
filename: "[path].gz[query]",
algorithm:"gzip",
test:productionGzipExtensions, //所有匹配此{RegExp}的资产都会被处理
threshold:512, // 只处理大于此大小的资产。以字节为单位
minRatio:0.8, //只有压缩好这个比率的资产才能被处理
deleteOriginalAssets:false //是否删除原资源
}))
}
}
CompressionPlugin
的主要配置和说明如上,此外需要特别注意的是deleteOriginalAssets
是否删除原资源,这里建议设置为false
即压缩资源和原始资源共存,以保证当客户端或者服务端不支持gzip压缩时能够返回原始资源,保障应用正常运行.否则可能出现404错误.更多配置请参见
🚀CompressionPlugin中文文档
- 查看压缩效果
-
服务器开启GZIP压缩
光是前端进行gzip压缩是不够的,同时需要开启服务端gzip共嫩,下面介绍下nginx和IIS服务器如何开启gzip压缩功能.
-
nginx服务器下开启gzip压缩
gzip on; gzip_static on; gzip_min_length 1k; gzip_buffers 4 32k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; gzip_disable "MSIE [1-6].";
配置说明:
gzip on on为启用,off为关闭 gzip_min_length 1k 设置允许压缩的页面最小字节数,页面字节数从header头中的Content-Length中进行获取。默认值是0,不管页面多大都压缩。建议设置成大于1k的字节数,小于1k可能会越压越大。 gzip_buffers 4 16k 获取多少内存用于缓存压缩结果,‘4 16k’表示以16k*4为单位获得 gzip_comp_level 5 gzip压缩比(1~9),越小压缩效果越差,但是越大处理越慢,所以一般取中间值; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php 对特定的MIME类型生效,其中'text/html’被系统强制启用 gzip_http_version 1.1 识别http协议的版本,早起浏览器可能不支持gzip自解压,用户会看到乱码 gzip_vary on 启用应答头"Vary: Accept-Encoding" gzip_proxied off nginx做为反向代理时启用,off(关闭所有代理结果的数据的压缩),expired(启用压缩,如果header头中包括"Expires"头信息),no-cache(启用压缩,header头中包含"Cache-Control:no-cache"),no-store(启用压缩,header头中包含"Cache-Control:no-store"),private(启用压缩,header头中包含"Cache-Control:private"),no_last_modefied(启用压缩,header头中不包含"Last-Modified"),no_etag(启用压缩,如果header头中不包含"Etag"头信息),auth(启用压缩,如果header头中包含"Authorization"头信息) gzip_disable msie6
-
IIS服务器配置GZIP( win server 2008R2 / IIS 7.5)
- 进入IIS管理器,点击图中压缩按钮
- 勾选压缩功能并应用
-
关于IIS服务器的特别说明
经实践证明,对于IIS服务器无需前端额外设置gzip压缩 也就是无需安装CompressionPlugin
并进行相关配置
GZIP 优化效果
通过gzip压缩资源大小从4.4M压缩至1.7MB finish load时间降到了3.8s效果显著
方法二: 减少请求次数
通过上图发现首屏加载过程中总共发起了291次请求,这显然是不友好的.减少首屏加载请求次数可考虑从以下几方面入手:
- 使用
SplitChunks
分离代码并实现相同模块共享,从而减少请求次数 vue-router
使用懒加载- 尽量避免组件库,UI库全局注入,最好是按需引用
使用SplitChunks
分离代码并实现相同模块共享,从而减少请求次数
- 安装
BundleAnalyzerPlugin
插件生成依赖包可视化分析图谱,帮助开发者分析项目结构
npm install --save-dev webpack-bundle-analyzer
vue.config.js
中进行BundleAnalyzerPlugin
插件配置
/** 开发环境 */
const DEV = process.env.NODE_ENV !== 'production';
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
publicPath : './' // 若要自动生成可视化分析页面,请将publicPath设置为相对路径
configureWebpack: config => {
/* **********************************************
*
* @description webpack 依赖包可视化分析
* 仅在性能分析的时候启用
* @Author: mingyong.g
* @Date: 2020-09-02 21:24:31
*
***********************************************/
//if(DEV){
config.plugins.push(new BundleAnalyzerPlugin())
//}
}
}
3. 运行npm run build --report 生成分析页面
npm run build --report
4. 配置SplitChunks
对注意模块进行分离
chainWebpack: config =>{
if (!DEV) {
config.optimization.splitChunks({
cacheGroups: {
common: {
name: 'chunk-common', // 打包后的文件名
chunks: 'all', //
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
reuseExistingChunk: true
},
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
priority: 2,
reuseExistingChunk: true,
enforce: true
},
ElementUI: {
name: 'chunk-element-ui',
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
chunks: 'all',
priority: 3,
reuseExistingChunk: true,
enforce: true
},
xlsx: {
name:"chunk-xlsx",
test: /[\\/]node_modules[\\/]xlsx[\\/]/,
chunks: 'all',
priority: 4,
reuseExistingChunk: true,
enforce: true
}
}
})
}
},
- 再次运行运行npm run build --report 查看结果
npm run build --report
已经好很多了,这时通过F2打开控制面板可以看到请求数已经只有130多次了,相比原始的291次,已经好很多了.同时请求资源降到了1.3Mb,load加载时间降到了1.56,finish时间仅1.77s(平均值在2s左右),
再来感受下首屏加载效果(ctrl+F5)并非本地刷新
路由懒加载
作为一个后台管理系统,路由较多使用路由懒加载是优化首屏加载的重要手段.路由懒加载设置比较简单,这里简单提及一下.在router.js文件中,原来的静态引用方式改为以函数的方式动态引入,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会下载路由组件
{
path: '/Login',
name: 'Login',
component: () = >import( /* webpackChunkName: "Login" */ '@/view/Login')
}
更细致的优化
本文到这里通过配置gzip / splitChunks / 路由懒加载已经实现了首屏加载10s到2s的性能提升,那么还有没有更进一步的优化空间呢?答案是有的,正如前面所说性能优化是一门很大的学问.以本项目为例,其实仍有很大的优化空间.下面先给出几张性能评估图再分析应该从那些方面着手进一步优化
1. webpack-bundle-analyzer分析图
2. lighthouse分析图
3. Coverage分析图
lighthouse分析图显示当前性能仅35%😭而其它项评分都还不错,继续分析 Coverage分析图可知再首页加载过程中约66% 的资源未被使用,也就是说可以通过优化这66% 的资源做到更好的首屏加载效果.
进一步优化主要思路在于减少全局注入
- Element 组件 按需引入
很多时候为了图方便将整个第三方库全局注入到项目中,比如Element UI, 其实更好的做法是按需应用,比如说项目仅用到了Button组件/select组件/table组件则仅在项目中引入这些组件即可,关于Element 按需引入可以参考🚀Element 按需引入
由于当前系统使用的Element 组件过多,可能本项优化暂不予考虑避免引起全局性,破坏性的错误
-
优化暂未使用的文件
首当其冲的就是webpack-bundle-analyzer分析图中显示的xlsx.js ,这个文件当时是参考element-admin 引入用于处理excel文件的第三方库,该库本身资源就很大,又由于我将其封装到全局组件中,故而首页加载时将其一并请求到客户端,这里也是一个主要的优化点,随着业务和需求变更,这个第三方库目前来看已经没有存在价值了,后续将考虑将其从代码从剥离,从而实现更好的加载体验. -
结合webpack-bundle-analyzer分析图 和
SplitChunks
插件对代码作更细致的拆分
需要注意的减少请求数必然使得单个文件体积变大,二者是矛盾的,最佳实践是取得一个中庸的值平衡优劣.