实战剖析 -vue 项目首屏加载时长优化
现状分析:
首屏速度是用户体验的最关键一环,而首屏速度最大的决定性因素就是资源的加载速度,资源加载速度等于资源大小 + 网速,老的前端项目随着不断增长,代码可能会变得混乱,冗余难以理解,不断的做加法,久而久之,前端性能上就会受到影响,相信大家在工作当中一定遇到,页面加载时间慢,响应时间长等问题,本文将以具体项目为例(vue 2.51.7 webpack:4.23.1),一点一点分析,通过实战的角度,介绍如何对 Vue 项目的首屏加载时间进行优化。
首先我们分析一下页面加载时间的构成:
(1)资源加载;
(2)代码执行;
(3)页面绘制;
打开 chrome 开发者模式面板,为了更好的还原用户的使用场景,我在设置-节流配置中添加模拟 5G 的性能分析选项,对网络状况做最大限度还原,开始录制页面加载过程,
通过性能分析工具,我们就能看到一共有哪些执行,花了多长时间,通过查看网络面板,可以查看到每一个资源的加载时间,脚本执行,页面渲染和绘制时间,可以看出等待加载资源过大,执行脚本时长是占据白屏时间的首要因素。也可利用 window.performance,以数据化的形式查看页面的各种时间,甚至可以把这些时间指标通过接口的形式发送给服务端,可以监控我们的项目在用户的终端设备中的表现;
问题找到了,就解决它
针对资源加载太慢的问题我先寻找大佬赏赐了一些思路:
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 默认是全包引入的
通过--report 参数 找到具体引入文件,发现尽管只用到了 lodash 的一个函数,但打包的体积也有几百 k,应该就是整包打的,没有按需打包,按照上述方式修改为按需打包,减少包体积,之后需要在.babelrc 里做一些配置,安装 npm install babel-plugin-lodash 插件,设置 plugins: ["lodash"]参数,之后打包文件就是按需打包的形式了。
说到神兵利器,不得不推荐 https://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() 函数来异步加载组件。
三、按需引入
现在的打包工具都有一个神兵利器叫做 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,由于网络或设备等因素有的用户可能出现三次刷新的情况
这个问题不解决,再多的优化都没有用,二次刷新的问题可能直接逼退一部分用户,于是开始着手改实现方案,第一步通过原生 js 实现内部依赖的功能(axios,路由取参,环境判断等...),脱离对项目中 npm 包的依赖,鉴于多个项目都在使用这个功能,采用 cdn 文件引入的方式,统一管理,项目内采用对构造函数的实例进行传参的方式,在 index.html 中引入,可以在最早的加载时机触发换 code 的逻辑,减短白屏等待时间,第二步通过对已授权用户的数据缓存,避免刷新页面请求,最大程度的降低了授权操作带来的耗时影响。
第五点和第六点一般我们不会有什么工作量,利用 nginx 访问资源的时候,一般都是会开启压缩的,利用 webpakc 或 vite 对代码进行压缩,也是我们利用 npm 打包的时候,自动进行压缩的,可以在 chrome 的 network 面板中的 Content-Encoding 这一栏中看到是否开启 gizp 压缩。
结尾
总的来说,性能优化是一个涵盖多个层面的综合性概念,对于不同业务场景下的前端项目而言,适用的方法也各不相同,上述的各种优化措施,是通过分析实际情况,定位性能瓶颈,并选取的适合的优化策略,感谢阅读😘
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/be55fd9cf0c2652077eac83a6】。文章转载请联系作者。
评论