常见性能表象
- 首屏UI显示缓慢
- 更新/操作UI显示卡顿
问题分析
- 首屏UI显示缓慢 —— 首屏渲染与资源加载、DOM构建、样式渲染、MVVM框架额外处理等相关,如资源过大、网络延迟、样式复杂、UI框架优化不足等
- 更新/操作UI显示卡顿 —— 此时影响界面渲染的主要因素是DOM操作、样式重排/绘、MVVM框架额外处理等,往往表现为样式渲染延迟、输入响应画面、页面出现假死或无响应,当然首屏渲染时也可能出现类似问题
汇总相关因素(排除网络稳定性等客观因素)
- js/css/图片等资源大小影响 (首屏)
- js同步逻辑过于复杂 (首屏/更新)
- DOM插入过多 (首屏/更新)
- CSS(动画)过多 (首屏/更新)
- MVVM框架负优化 (首屏/更新)
解决方案
-
资源加载(首屏)
对于资源文件拉取的优化无外乎利用新的浏览器特性以及对资源本身的容量优化,下面几种方式可以有效提升资源加载性能- 预加载
预加载支持多种类型资源,更多内容可查看文档 preload - HTML | MDN<link rel="preload" href="style.css" as="style" /> <link rel="preload" href="main.js" as="script" />
- 异步加载
可通过<script src="../tailwindcss3.4.3.js" async></script> <script src="../axios.min.js" defer></script>
async
或defer
关键字将脚本变为并行加载,关于两者区别可查看文档 <script> - HTML | MDN - 资源压缩
可对css/js等文本资源进行压缩,所有的主流工具如Webpack、Vite、Rollup等都提供了对应的配置,可大幅减少资源容量;可在服务端启用 GZIP 压缩减小资源尺寸; - 资源分割
可将一个大型资源分割为多个小型资源并通过异步加载减少因网络原因造成的影响 - 资源缓存(刷新时)
开启 Cache-Control - HTTP | MDN 对资源进行缓存,但需要考虑资源更新问题
Lighthouse指标
- 预加载
-
脚本执行(首屏/更新)
对于加载的脚本立即执行而造成页面阻塞卡顿的情况非常常见,可通过减少同步逻辑等方式来加快首屏渲染- 异步代码
大段的同步脚本会造成页面卡顿甚至假死,包括大对象拷贝、一次性DOM插入过多、频繁修改DOM树等,对于这类情况可在加载时使用骨架屏
或Loading层
,并将逻辑进行分片依次渲染对应UI块,对不影响后续操作的部分采用异步渲染 - 减小MVVM响应对象
尽量减少首屏MVVM组件中响应对象的容量,为大量对象/属性添加响应监控是非常消耗性能的 - 裁剪UI组件库
如果仅用到部分UI组件可以仅导出对应组件,而不是打包整个UI组件库
- 异步代码
-
DOM优化(首屏/更新)
即便现有浏览器引擎已经足够高效,未优化的DOM操作也会导致浏览器出现卡死甚至中止渲染的情况,下面是一些常用的DOM优化手段-
操作合并
DOM测试上图直观展示了合并操作后的性能差异,随着DOM结构的复杂度上升,耗时差距会更大
- 通过CSS
通常我们会用变更className来代替直接修改style,而有些场景还可以通过修改CssRule来实现同样的样式控制 —— 比如在通过div模拟的table组件中,通过样式控制列宽比逐个div设置width属性要高效的多,通过控制样式表可以用更小的代价实现效果变更 - 避免重排
重排是一种会阻塞页面的操作,对部分会导致重排的属性可以进行缓存,避免频繁查询重排属性。关于更多避免重排/重绘的方法可以查看 重排 - MDN Web 文档术语表:Web 相关术语的定义 | MDN
-
-
CSS动画(首屏/更新)
大量CSS动画会导致CPU使用率迅速上升,从而造成页面卡顿。如果必须使用Animation来展示效果务必注意以下几点- display属性
当元素设置了animation 且迭代次数是 infinite 时动画会持续执行即便页面看不到动画,除非display:none
。随着动画的数量,cpu占用率会显著提升。
监控指标
2.position属性
通过设置position属性为 absolute/fixed使元素脱离文档流后执行动画
- display属性
-
MVVM框架优化(首屏/更新)
MVVM框架通常使用setter/getter或者proxy进行响应属性的处理,避免初始化时大量创建响应属性可显著提升页面性能- 异步加载
避免在初始化中同步插入大量组件,如循环渲染表格这种复杂组件。多数MVVM框架都提供异步组件加载机制,可以分批加载 - 避免深层响应
尽量避免对深层对象进行响应监控
- 异步加载