现状分析:
首屏速度是用户体验的最关键一环,而首屏速度最大的决定性因素就是资源的加载速度,资源加载速度等于资源大小 + 网速,老的前端项目随着不断增长,代码可能会变得混乱,冗余难以理解,不断的做加法,久而久之,前端性能上就会受到影响,相信大家在工作当中一定遇到,页面加载时间慢,响应时间长等问题,本文将以具体项目为例(vue 2.51.7 webpack:4.23.1),一点一点分析,通过实战的角度,介绍如何对Vue项目的首屏加载时间进行优化。
首先我们分析一下页面加载时间的构成:
(1)资源加载;
(2)代码执行;
(3)页面绘制;
打开chrome开发者模式面板,为了更好的还原用户的使用场景,我在设置-节流配置中添加模拟5G的性能分析选项,对网络状况做最大限度还原,开始录制页面加载过程,
通过性能分析工具,我们就能看到一共有哪些执行,花了多长时间,通过查看网络面板,可以查看到每一个资源的加载时间,脚本执行,页面渲染和绘制时间,可以看出等待加载资源过大,执行脚本时长是占据白屏时间的首要因素。也可利用window.performance,以数据化的形式查看页面的各种时间,甚至可以把这些时间指标通过接口的形式发送给服务端,可以监控我们的项目在用户的终端设备中的表现;
javascript
window.onload = function(){
axios({
url: "xxx",
data: window.perfoformace
})
}
问题找到了,就解决它
针对资源加载太慢的问题我先寻找大佬赏赐了一些思路:
1、找到是哪个文件过大导致,是否可以拆包;2、如果不是马上需要的资源,可以异步加载;3、利用tree-shaking,尽量使用按需引入;4、定期实效数据可以缓存在cookies/localstorage中;5、进行gzip压缩;6、利用webpack / vite对代码进行压缩;
作为一只资深笨鸟,对大佬的思路领悟只有一二成,用最笨的方法,一条一条排查😬
为了直观的发现文件打包体积问题,可以查看打包时生成的报告,有两种方式:通过命令行参数的形式生成报告,也通过可视化的 UI 面板直接查看报告, npm run build --report的命令就可以生成报告,如图所示:在加上 --report命令后,可以生成 report.html来帮助分析包的内容,通过 http://127.0.0.1:8888/ 就可以查看打包报告里面的内容了 ,如图所示:
通过面板分析,app.js压缩后有960kb,发现nutui占据主包体积过大,pinyin.js文件重复多次引入,lodash需要实现按需引入等问题。。。
一、大文件拆包压缩
-
首先将索引文件通过cdn的方式引入,避免多模块重复引入问题,其次对nutui模块优化,不是首屏加载必须的模块可以按需引入,有一点我们需要注意在对nuti实现外部引入的时候,需要先对vue进行一个引入,因为如果不先引入vue,他就会在console里面报一个错误,原因与elementUI外部引入问题相同,采用cdn的方式引入后,我发现unpkg cnd负优化压缩比gzip还慢,参考大佬建议后,还是决定使用按需引入方式优化。
-
其次lodashi默认是全包引入的
javascript
//全包引入
import { cloneDeep } from "lodash";
//只引入cloneDeep函数
import cloneDeep from "lodash/cloneDeep";
通过--report参数 找到具体引入文件,发现尽管只用到了lodash的一个函数,但打包的体积也有几百k,应该就是整包打的,没有按需打包,按照上述方式修改为按需打包,减少包体积,之后需要在.babelrc里做一些配置,安装 npm install babel-plugin-lodash 插件,设置plugins: ["lodash"]参数,之后打包文件就是按需打包的形式了。
- 说到神兵利器,不得不推荐 tinify.cn/ 图片无损压缩工具,由于平时习惯直接从UI站点下载配图,对图片体积不是很敏感,它可以一次性导入批量无损压缩,一波操作下来,img资源从1.3M变成649K,着实很香。🤓
二、异步引入
目前来说比较新的脚手架版本,它生成的项目里面用到的一种prefetch 加载方案,简单介绍一下这种方案,假设有page1 page2 page3三个页面,1和2是同步加载的,3是异步加载的,那么异步加载的page3就会从app.js中单独拆出来作为一个文件模块,首页马上需要加载的文件都会放到app.js里,vendor.js里面就是page1和page2需要用到第三方库,page3中用到的第三方库就会标记一个perfecth,它都会在首屏打开的时候创建link标签去加载,有这个标记的资源会进入队列等待加载。
Vue 2.5.17 版本本身并不直接支持 prefetch 加载,然而,2.x 版本支持异步组件,这可以在某种程度上实现类似 prefetch 的行为,通过动态导入组件来按需加载。例如,可以使用 webpack 的 require.ensure 语法或者 import() 函数来异步加载组件。
javascript
components: {
..//
InsCalc: () => import('@/components/insCalc/insCalc')
..//
}
三、按需引入
- 现在的打包工具都有一个神兵利器叫做tree-shaking,可以把我们那些第三方库中只用到了的方法打包进去,但是很多库的老版本是不支持tree-shaking,比如我引入的xlsx的用来渲染excel表格,老的版本0.11.2的版本就不支持,我只使用 XLSX.utils.encode_cell / XLSX.utils.encode_range 的方法,打包的代码在压缩前体积就有七百kb,时间怎么也得延长几毫秒吧,升级到0.18之后就可以支持tree-shaking,一般情况下只要支持 import { utils } from "XLSX",都可以利用tree-shaking实现按需引入,这时候打包体积就变成了90kb了。👋
这一系列优化操作下来,再次通过分析工具,app.js主包文件已经有500kb了,虽然不算小,但也有进步了😝 ,加载时长已经变成了4秒左右了。
四、数据缓存
- 项目中有通过路由拦截的方式实现对用户授权数据进行分析的逻辑,这会直接导致在微信环境下二次跳转到微信生态获取code,由于网络或设备等因素有的用户可能出现三次刷新的情况
javascript
beforeRouteEnter(to, from, next) {
if (//非微信环境) {
next();
return;
}
const code = to.query.code;
if (!code) {
const redirectUrl = `https://jlkauth.jd.com/view?xxxx`;
const url = `https://open.weixin.qq.com/connect/oauth2/xxxxxx`;
} else {
//then 逻辑后才next() }
},
这个问题不解决,再多的优化都没有用,二次刷新的问题可能直接逼退一部分用户,于是开始着手改实现方案,第一步通过原生js实现内部依赖的功能(axios,路由取参,环境判断等...),脱离对项目中npm包的依赖,鉴于多个项目都在使用这个功能,采用cdn文件引入的方式,统一管理,项目内采用对构造函数的实例进行传参的方式,在index.html中引入,可以在最早的加载时机触发换code的逻辑,减短白屏等待时间,第二步通过对已授权用户的数据缓存,避免刷新页面请求,最大程度的降低了授权操作带来的耗时影响。
xml
<script src="https://cdn.../js/wxShareTrack.js"></script>
<script>
const wxTrack = new WxShareTrack();
const visit = {
visitTitle: '标题信息',
visitType: '类型'
};
wxTrack.wechatTrajectoryReport(visit);
../
</script>
- 第五点和第六点一般我们不会有什么工作量,利用nginx访问资源的时候,一般都是会开启压缩的,利用webpakc或vite对代码进行压缩,也是我们利用npm打包的时候,自动进行压缩的,可以在chrome的network面板中的Content-Encoding这一栏中看到是否开启gizp压缩。
结尾
总的来说,性能优化是一个涵盖多个层面的综合性概念,对于不同业务场景下的前端项目而言,适用的方法也各不相同,上述的各种优化措施,是通过分析实际情况,定位性能瓶颈,并选取的适合的优化策略,感谢阅读😘