性能优化指标
面试中的衍生问题常常是性能优化的指标有哪些
怎么定位白屏问题的
我们带着问题去看
用户访问页面白屏了,原因是啥,如何排查?
导致页面加载白屏时间长的原因有哪些,怎么进行优化?
目录
性能分析的工具
- Lighthouse
- web-vitals 官方标准
- performance
性能监控指标
- 白屏时间 FP
- 首次内容绘制时间 FCP
- 最大内容绘制时间 LCP
- 累积布局偏移值 CLS
- 首字节时间 TTFB
- 首次输入延迟时间 FID
- 可交互时间 TTI
- 总阻塞时间 TBT
- 首页加载时间
- 首屏加载时间
- DOM 渲染时间
- window.onload 时间
如何检测白屏
- 检测根节点是否渲染
- Mutation Observer 监听 DOM 变化
- 页面截图检测
- 采样对比
Core Web Vitals
Core Web Vitals
是应用于所有 Web
页面的 Web Vitals
的子集,所有的站点开发者都应该关注一下,他们将在所有谷歌提供的性能测试工具中进行显示。每个 Core Web Vitals
代表用户体验的一个不同方面,在该领域是可衡量的,并反映了以用户为中心的关键结果的真实体验
网页核心的性能指标应该是随着时间的推移而不断演变的。当前 2020
年主要关注用户体验的三个方面——加载、交互性和视觉稳定性:
Largest Contentful Paint(LCP):最大内容绘制。加载性能
指标。2.5s内加载完视为优秀
Interaction to Next Paint(INP):从交互到下一次绘制的延时。交互体验
指标。200ms 内视为优秀
Cumulative Layout Shift(CLS):累计布局偏移。视觉稳定性
指标,100ms内视为优秀
First Input Delay(FID):首次输入延时。交互体验
指标,100ms 内执行完视为优秀
PS:2024 年 3 月 INP 取代 FID
因为 FID 有一些局限性
- 首次:FID 只上报用户第一次与页面交互的响应性。虽然第一次交互很重要,但并不代表整个页面声明周期
- 输入延迟:FID 只测量首次交互的输入延时,即交互开始到事件开始处理这段时间,而事件处理和渲染的耗时,没有被统计
所以有了新的指标INP,它不仅测量首次交互,而且还测量所有的交互延时。除了输入延时,还包括事件处理时长、渲染延时。它的目标是确保从用户开始交互到下一帧绘制的时间尽可能短,以满足用户进行的所有或大多数交互
获取 Core Web Vitals
现在你可以使用标准的 Web API
在 JavaScript
中测量每个指标。Google
提供了一个 npm
包:web-vitals
,这个库提供了非常简单的 API
,测量每个指标就像调用一个普通函数一样简单:
npm install web-vitals
每个测量函数都接收一个 report
回调函数作为参数,回调函数将在测量完成后触发,另外,对于像 LCP
和 CLS
这样的指标是不断变化的,所以它们的回调函数可能会多次触发,如果你想获取在这期间获取每次变化的数值,你可以指定第二个参数 reportAllChanges
,否则回调函数只有在最终测量完成后触发一次。
import {getCLS, getFID, getLCP} from 'web-vitals';
getCLS(console.log, true);
getFID(console.log); // Does not take a `reportAllChanges` param.
getLCP(console.log, true);
以用户为中心的性能指标
- First Paint(FP):首次绘制
- 页面第一次绘制像素的时间,2s内优秀
- First contentful paint(FCP):首次内容绘制
- 首次渲染出内容(文本、图像等)的时间,理想值应小于1秒
- Largest contentful paint(LCP):最大内容绘制
- 视窗内最大元素绘制的时间,2.5s内优秀
- Interaction to Next Paint(INP):从交互到下一次绘制的延时。
- 交互体验指标。200ms 内视为优秀(代替 FID)
- First input delay(FID):首次输入延迟
- 当用户第一次与站点交互(点击按钮)的浏览器响应的时间,100ms内优秀
- 后被 INP 代替
- Time to interactive(TTI):可交互时间
- 用户首次可以与页面进行有意义互动的时间
- JS 未执行完、长任务阻塞
- Total blocking time(TBT):总阻塞时间
- FCP 到 TTI 之间长任务的阻塞时间,总体阻塞用户输入的时间段
- Cumulative layput shift(CLS):累积布局偏移
- 测量页面视觉稳定性
- DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像
<img>
和样式表之类的外部资源可能尚未加载完成 - load:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等
性能分析的工具
Lighthouse
Lighthouse 列出 Performance 各指标得分
Performance 列出了FCP、SP、LCP、TTI、TBI、CLS
六个指标的用时和得分情况
web-vitals 官方标准
web-vitals是 Googl e的一项计划,目的是为了统一衡量性能的标准,并提出一些核心性能指标,以此作为参考,从而定义页面的体验质量,而这些核心性能指标,就是Core Web Vitals。
官方指标标准
指标 | 作用 | 标准 |
---|---|---|
FCP(First Contentful Paint) | 首次内容绘制时间 | 标准 ≤1s |
LCP(Largest Contentful Paint) | 最大内容绘制时间 | 标准 ≤2 秒 |
FID(first input delay) | 首次输入延迟,标准是用户触发后,到浏览器响应时间 | 标准 ≤100ms |
CLS(Cumulative Layout Shift) | 累积布局偏移 | 标准 ≤0.1 |
TTFB(Time to First Byte) | 页面发出请求,到接收第一个字节所花费的毫秒数(首字节时间) | 标准<= 100 毫秒 |
应用监控
即用哪些软件可以监控性能
Lighthouse 分析:F12 中查看
PageSpeed Insights
PSI跑分内部也是集成了LightHouse,所以分值差异不大
白屏时间
白屏时间指的是浏览器开始显示内容的时间。因此我们只需要知道是浏览器开始显示内容的时间点,即页面白屏结束时间点即可获取到页面的白屏时间。
计算白屏时间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>白屏</title>
<script type="text/javascript">
// 不兼容performance.timing 的浏览器,如IE8
window.pageStartTime = Date.now();
</script>
<!-- 页面 CSS 资源 -->
<link rel="stylesheet" href="common.css">
<link rel="stylesheet" href="page.css">
<script type="text/javascript">
// 白屏时间结束点
window.firstPaint = Date.now();
</script>
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
因此白屏时间则可以这样计算出:
可使用 Performance API 时:
白屏时间 = firstPaint - performance.timing.navigationStart;
不可使用 Performance API 时:
白屏时间 = firstPaint - pageStartTime;
首屏时间
首屏时间是指用户打开网站开始,到浏览器首屏内容渲染完成的时间。对于用户体验来说,首屏时间是用户对一个网站的重要体验因素。通常一个网站,如果首屏时间在5秒以内是比较优秀的,10秒以内是可以接受的,10秒以上就不可容忍了。超过10秒的首屏时间用户会选择刷新页面或立刻离开。
通常计算首屏的方法有
- 首屏模块标签标记法
- 统计首屏内加载最慢的图片的时间
- 自定义首屏内容计算法
1、首屏模块标签标记法
首屏模块标签标记法,通常适用于首屏内容不需要通过拉取数据才能生存以及页面不考虑图片等资源加载的情况。我们会在 HTML 文档中对应首屏内容的标签结束位置,使用内联的 JavaScript 代码记录当前时间戳。如下所示:
[](javascript:void(0)😉
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首屏</title>
<script type="text/javascript">
window.pageStartTime = Date.now();
</script>
<link rel="stylesheet" href="common.css">
<link rel="stylesheet" href="page.css">
</head>
<body>
<!-- 首屏可见模块1 -->
<div class="module-1"></div>
<!-- 首屏可见模块2 -->
<div class="module-2"></div>
<script type="text/javascript">
window.firstScreen = Date.now();
</script>
<!-- 首屏不可见模块3 -->
<div class="module-3"></div>
<!-- 首屏不可见模块4 -->
<div class="module-4"></div>
</body>
</html>
此时首屏时间等于 firstScreen - performance.timing.navigationStart;
事实上首屏模块标签标记法 在业务中的情况比较少,大多数页面都需要通过接口拉取数据才能完整展示,因此我们会使用 JavaScript 脚本来判断首屏页面内容加载情况。
2、统计首屏内图片完成加载的时间
通常我们首屏内容加载最慢的就是图片资源,因此我们会把首屏内加载最慢的图片的时间当做首屏的时间。
由于浏览器对每个页面的 TCP 连接数有限制,使得并不是所有图片都能立刻开始下载和显示。因此我们在 DOM树 构建完成后将会去遍历首屏内的所有图片标签,并且监听所有图片标签 onload 事件,最终遍历图片标签的加载时间的最大值,并用这个最大值减去 navigationStart
即可获得近似的首屏时间。
此时首屏时间等于 加载最慢的图片的时间点 - performance.timing.navigationStart;
3、自定义模块内容计算法
由于统计首屏内图片完成加载的时间比较复杂。因此我们在业务中通常会通过自定义模块内容,来简化计算首屏时间。如下面的做法:
- 忽略图片等资源加载情况,只考虑页面主要 DOM
- 只考虑首屏的主要模块,而不是严格意义首屏线以上的所有内容
白屏检测方案实现流程
采样对比+白屏修正机制:
- 页面中间取 17 个采样点,利用 elementsFromPoint api 获取该坐标点下的 HTML 元素
- 定义属于容器元素的集合,如
['html', 'body', '#app', '#root']
- 判断 17 个采样点是否在该容器集合中。 说白了,就是判断采样点有没有内容;如果没有内容,该点的 dom 元素还是容器元素,若17个采样点都没有内容则算作白屏
- 若初次判断是白屏,开始轮询检测,来确保白屏检测结果的正确性,直到页面的正常渲染