背景:近期开展了lighthouse培训,并以此为契机提高项目性能。以lighthouse分析报告为基础,并从网页生命周期角度从四个方面进行着手提升性能。
lighthouse性能指标
- FCP。FCP(First Contentful Paint)即首次内容绘制。它统计的是从进入页面到首次有 DOM 内容绘制所用的时间。这里的 DOM 内容指的是文本、图片、非空的 canvas 或者 SVG。也就是我们常说的白屏时间。
- LCP。LCP(Largest Contentful Paint)即最大内容绘制。它统计的是从页面开始加载到视窗内最大内容绘制的所需时间,这里的内容指文本、图片、视频、非空的 canvas 或者 SVG 等。也就是首屏时间。
- SI。SI(Speed Index)即速度指数。Lighthouse 会在页面加载过程中捕获视频,并计算视频中帧与帧之间视觉变化的进度,这个指标反映了网页内容填充的速度。页面解析渲染过程中,资源的加载和主线程执行的任务会影响到速度指数的结果。
- CLS。CLS(Cumulative Layout Shift)即累计布局位移。这个指标是通过比较单个元素在帧与帧之间的位置偏移来计算。我们希望页面的视觉呈现保持相对稳定,比如,不会突然插入一张图片或者元素突然发生位移。
- TTI。TTI(Time To Interactive)即页面可交互的时间。这个时间的确定需要同时满足以下几个条件:
- 页面开始绘制内容,即 FCP 指标开始之后
- 用户的交互可以及时响应
- 页面中大部分可见的元素已经注册了对应的监听事件(通常在 DOMContentLoaded 事件之后)
- 在 TTI 之后持续 5 秒的时间内无长任务执行(没有超过 50 ms 的执行任务 ), 没有超过 2 个 GET 请求
- TBT。TBT(Total Blocking Time)即阻塞总时间,测量的是 FCP 与 TTI 之间的时间间隔。这个指标反映了用户的交互是否能及时响应。 如果主线程执行了长任务会导致用户的输入无法及时响应。假设在主线程上执行了一系列的任务,每个长任务(超过 50 ms 的执行任务)的阻塞时间等于执行时间减去 50 ms。
- TBT和TTI参数中涉及到长任务这个概念。长任务与微任务宏任务无关,长任务是指"时间段",在这个时间段内主UI线程做耗时达到或超过50ms的事情(MDN定义是A long task is any uninterrupted period where the main UI thread is busy for 50ms or longer。根据后面的MDN举的场景来看,主UI线程应该是指常说的GUI线程,也可以把长任务理解为GUI线程中做的耗时达到或超过50ms的事情,50ms就是页面绘制卡顿的预警值。低于20帧时用户交互就比较能感觉出来卡顿了。关于50ms如何来的,这是与RAIL性能模型相关,可以看文章:RAIL性能模型)。常见场景:比如相邻时间循环之间执行的事情;比如重排重绘。在performance面板可以测量性能,测量结果中在Main栏中灰色并带有斜杠红色的任务就是长任务,鼠标放在长任务上面可以看到耗时,点击该长任务,在底部的Bottom-Up可以看到任务详情,见下图。
加载优化
- gzip压缩文本资源。参考# 前端性能提升之gzip压缩以及# 使用compression-webpack-plugin。
- 使用http2协议。可以提高TBT(Time To Interactive)值,较明显地提高lighthouse评估得分。参考# vue项目配置https--通过keytool生成ssl证书并在nginx配置https,并修改nginx设置"listen 443 ssl http2",在Network面板查看Protocol列发现值为h2即表示开启成功。
- 对需要渲染多个图片的页面使用懒加载,比如使用IntersectionObserver减少http请求,避免不必要在首屏展示的非关键性信息占用页面加载时间。
构建优化
用主流构建工具webpack/vite,合并资源,压缩资源,减少http请求数量和数据包大小。参考# 前端性能提升之webpack打包优化。
缓存优化
对文件名含hash的文件使用强缓存,避免重复请求。参考# 前端性能提升之强缓存。
渲染优化
减少不必要的重排和重绘。比如echarts图表在resize时使用防抖;输入框根据输入框中的值加载后端接口数据时使用节流。对文件名含hash的文件使用强缓存,避免重复请求;表格组件渲染大数据量时使用虚拟滚动,比如naive ui的表格组件n-data-table通过设置virtual-scroll和max-height属性开启虚拟滚动。