对互动的响应能力不佳,会给用户留下页面运行缓慢甚至完全损坏的糟糕印象。Interaction to Next Paint (INP) 是一项基于 Event Timing API 的指标。它会衡量最差的互动(不包括一些离群值),以此作为网页响应速度的衡量指标。
找出网页无响应的根本原因可能很棘手,尤其是因为这取决于用户互动和环境条件(例如设备功能和网络条件)。与网页加载等更具可重复性和可预测性的场景相比,这使得诊断变得更加困难。实验室数据只能模拟一小部分真实用户场景,因此在实际环境中衡量 INP 缓慢的原因至关重要。
Event Timing API 允许将 INP 时长拆分为三个子部分:输入延迟(互动发生时已在进行的处理)、处理时长(互动直接导致的延迟)和呈现延迟(互动后渲染下一帧导致的延迟)。这有助于确定问题是出在其他代码、互动 JavaScript 代码还是浏览器处理渲染更新上,而不是直接跳到互动代码。
您可以进一步了解导致互动延迟的 JavaScript、样式重新计算和布局工作。使用 JS Self-Profiling API 的完整性能跟踪是一种重量级解决方案,很容易导致性能问题。长动画帧 API 是一种轻量级 API,可在现场使用,用于识别运行缓慢的 JavaScript,并区分 INP 互动期间的样式重新计算和布局开销:
- 脚本级强制样式和布局(
forcedStyleDuration和forcedLayoutDuration):报告在输入延迟或处理时长期间,正在运行的脚本中发生的同步样式重新计算(例如在 DOM 更改后调用getComputedStyle())和强制布局/重排(例如在更改后读取getBoundingClientRect()或offsetWidth)。 - 帧级渲染样式和布局(
styleDuration和layoutDuration):报告在帧的渲染更新期间,重新计算样式与更新布局所花费的时间,有助于解释呈现延迟时间较长的原因。
区分样式重新计算和布局至关重要,因为它们的根本原因和解决方法不同:样式时长过长表明 CSS 选择器匹配开销过高或样式失效范围过广(可通过简化选择器或缩小规则范围来解决),而布局时长过长则表明几何计算开销过高或同步布局抖动(可通过在写入前批量读取 DOM、简化 DOM 结构或使用 CSS 包含来解决)。
如何实现
通过原始 Event Timing API 计算 INP 非常复杂,并且存在一些细微差别。建议您使用 RUM 工具或库来收集这些数据。
web-vitals 是 Google 提供的一个开源库,用于计算 Core Web Vitals(包括 INP),还包含 INP 互动的子部分和长动画帧数据。
使用 web-vitals 库获取 INP 互动的归因
web-vitals 库是一个小型库,用于衡量 Core Web Vitals 和其他效果指标。onINP() 函数可用于识别最慢的互动,并包含有关 INP 子部分、互动期间执行的脚本以及使用长动画帧 API 的样式和布局持续时间的信息。
// Use the attribution build to get Long Animation Frame data
// alongside the INP metric value.
import { onINP } from 'web-vitals/attribution';
onINP((metric) => {
const { attribution } = metric;
const longestScript = attribution.longestScript;
// Sum rendering-phase style and layout durations across all LoAF entries
// intersecting the INP interaction (primarily explains presentationDelay).
let renderStyleDuration = 0;
let renderLayoutDuration = 0;
for (const entry of attribution.longAnimationFrameEntries) {
renderStyleDuration += entry.styleDuration ?? 0;
renderLayoutDuration += entry.layoutDuration ?? 0;
}
// Beacon script, style, and layout attribution for the INP interaction,
// so you can identify the root cause in production.
navigator.sendBeacon(
'/analytics',
JSON.stringify({
name: 'INP',
value: metric.value,
// These fields give the INP subparts:
inputDelay: attribution.inputDelay,
presentationDelay: attribution.presentationDelay,
processingDuration: attribution.processingDuration,
interactionTarget: attribution.interactionTarget,
// These fields identify which script function was responsible
// for the longest processing during the INP interaction.
invokerType: longestScript?.entry?.invokerType,
sourceURL: longestScript?.entry?.sourceURL,
sourceFunctionName: longestScript?.entry?.sourceFunctionName,
sourceCharPosition: longestScript?.entry?.sourceCharPosition,
// subpart indicates which phase (input delay, processing, or
// presentation delay) the longest script overlapped with most.
subpart: longestScript?.subpart,
intersectingDuration: longestScript?.intersectingDuration,
// Synchronous style and layout work forced inside the longest script
// (with forcedStyleAndLayoutDuration as a combined fallback):
forcedStyleDuration: longestScript?.entry?.forcedStyleDuration,
forcedLayoutDuration: longestScript?.entry?.forcedLayoutDuration,
forcedStyleAndLayoutDuration: longestScript?.entry?.forcedStyleAndLayoutDuration,
// Rendering-phase style and layout work across the interaction's frames
// (with totalStyleAndLayoutDuration as a combined fallback):
renderStyleDuration,
renderLayoutDuration,
totalStyleAndLayoutDuration: attribution.totalStyleAndLayoutDuration
})
);
});
最佳做法
- 务必先使用 INP 子部分来确定延迟是由于已经在运行 JavaScript(输入延迟)、互动的事件处理脚本 JavaScript(处理时长)还是后续渲染(呈现延迟)造成的。
- 务必尝试找出造成最大阻塞的 JavaScript,并提供更多详细信息,尤其是输入延迟时间和处理时长。
- 务必在诊断处理时长过长或呈现延迟时,区分样式重新计算(
styleDuration、forcedStyleDuration)和布局(layoutDuration、forcedLayoutDuration):样式计算时间过长表示 CSS 选择器开销过大或失效范围过广,而布局时间过长则表示 DOM 几何结构存在瓶颈或同步布局抖动。 - 务必优先使用 Long Animation Frames API 来提供这些更详细的信息,而不是使用 JS Self-Profiling API 等运行时开销更高的替代方案。
- 务必在没有其他 RUM 解决方案的情况下使用
web-vitals库。它可识别 INP 互动,并包含有关互动期间执行的子部分和脚本的信息(使用 Long Animation Frames API)。 - 务必将所需信息信标回分析服务,而不仅仅是在本地记录。
浏览器支持和后备策略
大多数现代浏览器都支持 Event Timing API,该 API 是计算 INP 及其子部分所必需的。对于不支持此 API 的浏览器,无法衡量 INP。
Long Animation Frames API 提供了可选的其他详细信息。无需后备方案即可安全使用。在大多数情况下,它发现的性能提升机会也适用于其他浏览器。
如果您的基准目标支持 Long Animation Frames API (long-animation-frames),但不支持单独的样式和布局时长属性(PerformanceLongAnimationFrameTiming 上的 styleDuration 和 layoutDuration,或 PerformanceScriptTiming 上的 forcedStyleDuration 和 forcedLayoutDuration),请回退到组合样式和布局时间:
- 在
PerformanceScriptTiming上使用script.forcedStyleAndLayoutDuration,以便在脚本中执行同步强制样式和布局工作。 - 在
web-vitals/attribution中使用metric.attribution.totalStyleAndLayoutDuration(或在每个PerformanceLongAnimationFrameTiming条目上计算(entry.paintTime || entry.startTime + entry.duration) - entry.styleAndLayoutStart),以实现样式和布局的组合。