blog
blog copied to clipboard
Web性能指标与相关优化
现在用Chrome DevTools的Performance Insights可以查看页面的性能表现:

本文探讨上面涉及到的基础性能指标(可能更多),以及对应的优化方式。
性能指标历史
在Performance等API出现之前,浏览器端和性能相关的有两个时间点:
-
DOMContentLoaded:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待图像等等完全加载。通常情况下这个时间点是DOM和CSSOM完成。
-
load:在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。它与 DOMContentLoaded 不同,后者只要页面 DOM 加载完成就触发,无需等待依赖资源的加载。
这两个时间点无法准确全面地描述用户感受的页面性能。我们通常会通过额外的代码来计算得到白屏、首屏时间等。
- 白屏时间:页面请求发出到绘制第一个页面元素的时间。
- 通常认为浏览器开始渲染
<body>或者解析完<head>的时间是白屏结束的时间点。
- 通常认为浏览器开始渲染
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
// 不兼容 performance.timing 的浏览器
window.pageStartTime = Date.now()
</script>
<!-- CSS 和其它JS资源 -->
<link rel="stylesheet" href="xx.css">
<link rel="stylesheet" href="zz.css">
<!-- 放head的最后面或者body最前面 -->
<script>
// 白屏结束时间
window.firstPaint = Date.now()
// 白屏时间
const blankPageDuration = firstPaint - (performance.timing.navigationStart || window.pageStartTime)
</script>
</head>
<body>
body
</body>
</html>
- 首屏时间:页面请求发出到首屏绘制完成的时间。
- 首屏时间的关键在于怎么定义首屏绘制完成。可以用首屏里面最慢图片加载完成作为结束;或者手动在首屏结束的地方插入结束标签并计时。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
// 不兼容 performance.timing 的浏览器
window.pageStartTime = Date.now()
</script>
</head>
<body>
<p>各种首屏内容</p>
<!-- 认为首屏结束,插入script来计时 -->
<script>
// 首屏结束时间
window.firstScreenPaint = Date.now()
// 首屏时间
const blankPageDuration = firstScreenPaint - (performance.timing.navigationStart || window.pageStartTime)
</script>
</body>
</html>
基础性能指标
1. First Contentful Paint 首次内容绘制 (FCP)
首次内容绘制 (FCP) 指标测量页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,"内容"指的是文本、图像(包括背景图像)、<svg>元素或非白色的<canvas>元素。

好的FCP时间应该小于1.8秒。那怎么监测FCP?
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntriesByName('first-contentful-paint')) {
console.log('FCP candidate:', entry.startTime, entry);
}
}).observe({type: 'paint', buffered: true});
更完善的可以参考onFCP()的源代码。
怎么优化FCP?
- 消除阻塞渲染的资源
- 缩小 CSS
- 移除未使用的 CSS
- 预连接到所需的来源
- 减少服务器响应时间 (TTFB)
- 避免多个页面重定向
- 预加载关键请求
- 避免巨大的网络负载
- 使用高效的缓存策略服务静态资产
- 避免 DOM 过大
- 最小化关键请求深度
- 确保文本在网页字体加载期间保持可见
- 保持较低的请求数和较小的传输大小
2. Largest Contentful Paint 最大内容绘制 (LCP)
最大内容绘制 (LCP) 指标会根据页面首次开始加载的时间到可视区域内可见的最大图像或文本块完成渲染的相对时间。
最大内容绘制 (LCP) 是测量感知加载速度的一个以用户为中心的重要指标,因为该项指标会在页面的主要内容基本加载完成时,在页面加载时间轴中标记出相应的点,迅捷的 LCP 有助于让用户确信页面是有效的。

好的LCP时间应该小于2.5秒。那怎么监测LCP?
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
怎么优化 LCP?
- 使用 PRPL 模式做到即时加载
- 优化关键渲染路径
- 优化您的 CSS
- 优化您的图像
- 优化网页字体
- 优化您的 JavaScript(针对客户端渲染的网站)
3. First Input Delay 首次输入延迟 (FID)
FID 测量从用户第一次与页面交互(例如当他们单击链接、点按按钮或使用由 JavaScript 驱动的自定义控件)直到浏览器对交互作出响应,并实际能够开始处理事件处理程序所经过的时间。

好的FID应小于100毫秒。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
const delay = entry.processingStart - entry.startTime;
console.log('FID candidate:', delay, entry);
}
}).observe({type: 'first-input', buffered: true});
怎么优化FID?
4. Time to Interactive 可交互时间 (TTI)
TTI 指标测量页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入所需的时间。
怎么测量TTI?
- 先进行First Contentful Paint 首次内容绘制 (FCP)。
- 沿时间轴正向搜索时长至少为 5 秒的安静窗口,其中,安静窗口的定义为:没有长任务且不超过两个正在处理的网络 GET 请求。
- 沿时间轴反向搜索安静窗口之前的最后一个长任务,如果没有找到长任务,则在 FCP 步骤停止执行。
- TTI 是安静窗口之前最后一个长任务的结束时间(如果没有找到长任务,则与 FCP 值相同)。

虽然 TTI 可以在实际情况下进行测量,但我们不建议这样做,因为用户交互会影响您网页的 TTI,从而导致您的报告中出现大量差异。如需了解页面在实际情况中的交互性,您应该测量First Input Delay 首次输入延迟 (FID) 。
怎么优化TTI?
5. Time to First Byte 第一字节时间 (TTFB)
TTFB 是一个衡量对资源的请求和响应的第一个字节开始和到达之间时间的指标。

怎么测量TTFB?
new PerformanceObserver((entryList) => {
const [pageNav] = entryList.getEntriesByType('navigation');
console.log(`TTFB: ${pageNav.responseStart}`);
}).observe({
type: 'navigation',
buffered: true
});