以用户为中心的性能指标是理解和改进站点体验的关键点
指标是用来衡量性能和用户体验的
感知加载速度:网页可以多快地加载网页中的所有视觉元素并将其渲染到屏幕上
加载响应速度:页面加载和执行组件快速响应用户互动所需的 JavaScript
代码的速度
运行时响应速度:网页在加载后对用户互动的响应速度
视觉稳定性:页面上的元素是否会以用户意想不到的方式发生偏移,是否可能会干扰用户的互动?
流畅性:过渡和动画是否以一致的帧速率渲染,并在一种状态之间流畅地流动?
从网页开始加载到网页内容的任何部分呈现在屏幕上所用的时间
从网页开始加载到屏幕上呈现最大的文本块或图片元素所用的时间
与网页进行的每次 tap
、click
或键盘互动的延迟时间
并根据交互的数量选择页面中最差的交互延迟作为单个代表性值来描述页面的总体响应性
从 FCP
到可交互时间 (TTI
) 之间的总时长
从页面开始加载到其生命周期状态更改为隐藏期间发生的所有意外布局偏移的累计分数
网络使用资源的第一个字节响应用户请求所花费的时间
用户首次与网页互动(即,点击链接、点按按钮或使用由 JavaScript
提供支持的自定义控件)到浏览器实际能够开始处理事件处理脚本以响应相应互动的时间
FCP
:从网页开始加载到网页内容的任何部分呈现在屏幕上所用的时间。
首次内容绘制 (FCP
) 是一项以用户为中心的重要指标,用于衡量感知的加载速度。
它标记了网页加载时间轴中用户可以看到屏幕上任何内容的第一个点。
FCP
衡量的是从用户首次导航到相应网页到该网页的任何部分呈现在屏幕上所用的时间。对于此指标,内容是指文本、图片(包括背景图片)、<svg>
元素或非白色 <canvas>
元素。
FCP
在第二帧发生,因为这是第一个文本元素渲染到屏幕上的时间
为了提供良好的用户体验,网站的 FCP
最好不超过 1.8 秒。
较好的 FCP
值为 1.8 秒或更短。较差超过 3.0 秒。
Lighthouse
Chrome DevTools
如需在 JavaScript
中测量 FCP
,需使用 Paint Timing API
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntriesByName('first-contentful-paint')) {
console.log('FCP candidate:', entry.startTime, entry);
}
}).observe({type: 'paint', buffered: true});
API
会为后台标签页中加载的网页分派 first-contentful-paint
,但在计算 FCP
时应忽略这些网页。只有当网页始终在前台运行时,系统才会考虑首次渲染时间。
从往返缓存中恢复网页时,API
不会报告 first-contentful-paint
,但在这些情况下,应衡量 FCP
,因为用户将它们视为不同的网页访问。
API
可能不会报告跨源 iframe
的绘制时间,但为了正确衡量 FCP
,必须考虑所有帧。子帧可以使用该 API
将其绘制时间报告给父帧以进行汇总。
API
从导航启动时开始测量 FCP
,但对于预渲染的网页,应通过 activationStart
测量 FCP
。
使用 web-vitals JavaScript
库来衡量 FCP
,而无需记住所有这些细微差异,该库会尽可能处理这些差异
import {onFCP} from 'web-vitals';
onFCP(console.log);
onFCP
源码
可以运行 Lighthouse
性能审核,并关注审核建议的任何特定 opportunities
或者 diagnostics
。
移除阻塞渲染的资源
缩减 CSS
大小
移除未使用的 CSS
移除未使用的 JavaScript
预先连接到所需的源
缩短服务器响应时间 (TTFB
)
避免多次网页重定向
预加载密钥请求
避免网络负载庞大
采用高效的缓存政策提供静态资源
避免 DOM
规模过大
最大限度地缩短关键请求深度
确保文本在网页字体加载期间保持可见状态
尽量减少请求数量,减少传输大小
LCP
:从网页开始加载到屏幕上呈现最大的文本块或图片元素所用的时间
LCP
报告的是窗口中可见最大图片或文本块相对于用户首次导航到网页的呈现时间
LCP
包含从上一个网页开始的所有卸载时间、连接设置时间、重定向时间和首字节时间 (TTFB
)
为了提供良好的用户体验,网站应努力将 LCP
控制在 2.5 秒以内。
2.5 s 或者更短
Largest Contentful Paint
考虑的元素类型包括:
<img>
元素(第一帧呈现时间用于 GIF
或动画 PNG
等动画内容)
<svg>
元素内的 <image>
元素
<video>
元素(系统会使用视频的海报图片加载时间或第一帧显示时间,以较早者为准)
一个元素,带有使用 url()
函数加载的背景图片
包含文本节点或其他内嵌级文本元素子元素的块级元素。
后面可能会增加其他元素
为 LCP
报告的元素的大小通常是用户在窗口中可见的大小,如果元素延伸到窗口之外,或者元素被剪切或有不可见的溢出,这些部分不计入元素的大小。
对于根据固有尺寸调整过大小的图片元素,报告的尺寸为可见尺寸或固有尺寸(以较小者为准)。
对于文本元素,LCP
只会考虑能够包含所有文本节点的最小矩形。
对于所有元素,LCP
都不会考虑使用 CSS
应用的外边距、内边距或边框。
网页通常会分阶段加载,因此,网页上最大的元素可能会发生变化。
为了应对这种可能发生的变化,浏览器在绘制完第一帧后,会立即分派 largest-contentful-paint
类型的 PerformanceEntry
,用于标识链接最大的内容元素,在渲染后续帧后,只要最大内容元素发生变化,它就会再分派一个 PerformanceEntry
元素只有在呈现并对用户可见后,才能被视为最大的内容元素。尚未加载的图片不会被视为已渲染。在字体屏蔽期间,文本节点也不会使用网页字体。在这种情况下,系统可能会将较小的元素报告为最大的内容元素,但如果这个较大的元素呈现完毕,系统便会创建另一个 PerformanceEntry
除了延迟加载图片和字体之外,网页还可能会在新内容可用时向 DOM
添加新元素。如果这些新元素中的任何一个大于之前的最大内容元素,系统还会报告新的 PerformanceEntry
如果从窗口甚至 DOM
中移除了最大的内容元素,除非渲染了较大的元素,否则它仍然是最大的内容元素
一旦用户与网页互动(通过点按、滚动或按键),浏览器就会停止报告,因为用户互动通常会改变向用户显示的内容(特别是滚动时)。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
API
将为后台标签页中加载的页面分派 largest-contentful-paint
,但在计算 LCP
时应忽略这些页面。
在页面进入后台后,API
将继续分派 largest-contentful-paint
,但在计算 LCP
时应忽略这些(只有在页面始终在前台运行时才可以考虑元素)。
从往返缓存中恢复网页时,该 API
不会报告 largest-contentful-paint
,但应在在这些情况下衡量 LCP
,因为用户对它们的访问体验是不同的。
API
不会考虑 iframe
中的元素,但该指标会考虑,因为它们是网页用户体验的一部分。
API
从导航开始就测量 LCP
,但对于预渲染的网页,LCP
应从 activationStart
开始测量,因为 LCP
对应于用户实际体验到的 LCP
时间。
可以使用 web-vitalsJavaScript
库来衡量 LCP
,而无需记住所有这些细微差异
import {onLCP} from 'web-vitals';
onLCP(console.log);
onLCP
源码
INP
:与网页进行的每次 tap
、click
或键盘互动的延迟时间
良好的响应速度意味着网页对互动的响应速度很快。
当网页响应互动时,浏览器会在所绘制的下一帧中提供_视觉反馈。
有些互动自然会比其他互动花费更长的时间,但对于特别复杂的互动,必须快速提供一些初始视觉反馈,让用户知道正在发生的事情。
INP
的目的不是测量互动的所有最终效果,而是下一次绘制被阻止的时间。通过延迟视觉反馈,用户可能会觉得页面响应速度不够快,而 INP 旨在帮助开发者衡量这部分用户体验。
一般 200 ms 以内
INP
低于或等于 200 毫秒表示网页响应良好。
INP
高于 200 毫秒且低于或等于 500 毫秒表示网页的响应能力需要改进。
INP
高于 500 毫秒表示网页响应速度很差。
互动的主要驱动因素通常是 JavaScript
,但浏览器确实会通过并非由 JavaScript
提供支持的控件(例如复选框、单选按钮和由 CSS
提供支持的控件)提供互动性。
就 INP
而言,只观察以下互动类型:
互动发生在主文档或文档内嵌的 iframe
中。
系统会在用户离开页面时计算该页面的 INP
。结果会得到一个能够代表网页在其整个生命周期内的整体响应能力的值。INP
较低意味着网页能够可靠地响应用户输入。
INP
是 First Input Delay (FID)
的继任指标。虽然两者都是响应速度指标,但 FID
仅衡量了网页上首次互动的输入延迟。INP
通过观察网页上的所有互动来改进 FID
,即从输入延迟开始,到运行事件处理脚本所需的时间,再到浏览器绘制下一帧。
这些差异意味着 INP
和 FID
是不同类型的响应能力指标。FID
是用于评估网页对用户的首次展示的加载响应速度指标,而无论网页互动在何时发生,INP
都是更可靠的整体响应能力指标。
网页可能不会返回任何 INP
值。导致这种情况的原因可能有很多,其中包括以下原因:
优化耗时较长的任务
优化输入延迟
脚本评估和耗时较长的任务
使用 Web Worker
在浏览器的主线程之外运行 JavaScript
避免大型、复杂的布局和布局抖动
缩小样式计算的范围并降低其复杂性
TBT
:总阻塞时间,从 FCP
到可交互时间 (TTI
) 之间的总时长,其中主线程处于阻塞状态的时间足够长,足以阻止输入响应能力。
每当存在长任务(一种在主线程上运行时间超过 50 毫秒 (ms
) 的任务)时,主线程就会被视为阻塞。
我们说主线程处于阻塞状态,因为浏览器无法中断正在进行的任务。如果用户尝试在耗时较长的任务过程中与页面互动,浏览器必须等待任务完成才能响应。
如果主线程处于阻塞状态的时间超过 50 毫秒,用户很可能会注意到延迟,并认为网页运行缓慢或损坏。
网站的 TBT
应低于 200 毫秒。
TBT 时间(以毫秒为单位) | 颜色编码 |
---|---|
0 - 200 | 绿色(快速) |
200-600 | 橙色(中等) |
600 多个 | 红色(慢) |
将所有工作拆分为运行时间不超过 50 毫秒的代码块,并在合适的位置和时间运行这些代码块。
降低第三方代码的影响
缩短 JavaScript
执行时间
去除不必要的 JS
加载、解析、执行
尽量减少主线程工作
尽量减少请求数量,减少传输大小
CLS
:从页面开始加载到其生命周期状态更改为隐藏期间发生的所有意外布局偏移的累计得分。
每当可见元素的位置从渲染的帧更改为下一帧时,都会发生布局偏移。
布局偏移由 Layout Instability API
定义。
只要窗口内可见的元素在两帧之间更改起始位置,该 API
就会报告 layout-shift
。此类元素被视为不稳定元素。
当资源以异步方式加载或 DOM
元素被动态添加到页面的现有内容之前,页面内容通常会发生意外移动。
布局偏移的原因可能包括尺寸未知的图片或视频、呈现的字体大于或小于其初始后备值,或者第三方广告或微件会自行调整大小。
由于网站在开发过程中的运行情况与其用户的体验之间的差异,会使此问题变得更糟。例如:
个性化内容或第三方内容在开发和生产环境中的行为通常有所不同。
测试图片通常已存在于开发者的浏览器缓存中,但为最终用户加载所需时间更长。
在本地运行的 API
调用速度通常非常快,以至于开发过程中出现明显的延迟,在生产环境中可能就会出现严重的延迟。
为了提供良好的用户体验,网站应努力使 CLS 得分不超过 0.1。
Chrome
用户体验报告
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('Layout shift:', entry);
}
}).observe({type: 'layout-shift', buffered: true});
如果网页是在后台加载的,或者在浏览器绘制任何内容之前在后台播放,则系统不应报告任何 CLS
值。
如果某个网页从往返缓存中恢复,其 CLS
值应重置为零,因为用户此次体验是一次不同的网页访问。
该 API
不会针对 iframe
中发生的偏移报告 layout-shift
,但该指标会报告这些变化,因为它们会影响网页的用户体验。
可以使用 web-vitalsJavaScript
库来衡量 CLS
import {onCLS} from 'web-vitals';
onCLS(console.log);
onCLS
源码
改善没有尺寸的图片
给嵌入内容的延迟加载预留空间
动画:尽量使用 transform
平移动画、缩放、旋转/倾斜元素
网络字体优化
TTFB
:网络使用资源的第一个字节响应用户请求所需的时间。
TTFB
是以下请求阶段的总和:
重定向时间
Service Worker
启动时间(如果适用)
DNS
查找
连接和 TLS
协商
请求,直到响应的第一个字节到达
缩短连接设置时间和后端的延迟时间有助于降低 TTFB
。
大多数网站都应尽量将 TTFB
控制在 0.8 秒以内。
Chrome
用户体验报告web-vitalsJavaScript
库WebPageTest
PerformanceObserver
方法new PerformanceObserver((entryList) => {
const [pageNav] = entryList.getEntriesByType('navigation');
console.log(`TTFB: ${pageNav.responseStart}`);
}).observe({
type: 'navigation',
buffered: true
});
使用 web-vitalsJavaScript
库也可以在浏览器中以较低的复杂性测量 TTFB
import {onTTFB} from 'web-vitals';
onTTFB(console.log);
onTTFB
源码
具体的项目平台
托管服务商
CDN
尽可能的使用缓存
避免多次重定向
使用 service worker
FID
:从用户首次与网页互动到浏览器实际能够开始处理事件处理脚本以响应相应互动的时间。
为了提供良好的用户体验,网站应努力将 FID
控制在 100 毫秒以内
FID
指标只能在实际操作衡量,因为它需要真实用户的网页互动
Chrome
用户体验报告
PageSpeed Insights
web-vitals JavaScript
库
PerformanceObserver
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});
import {onFID} from 'web-vitals';
onFID(console.log);
onFID
源码
拆分长任务
使用 web worker
缩短 JS
执行时间
减少多余的 JS
针对具体的情况优化页面