了解如何优化网站的 Interaction to Next Paint。
发布时间:2023 年 5 月 19 日;上次更新时间:2025 年 9 月 2 日
Interaction to Next Paint (INP) 是一项稳定的核心网页指标,用于通过观测用户访问网页期间发生的所有符合条件的互动的延迟时间,评估网页对用户互动的总体响应情况。最终 INP 值是观测到的最长互动时间(有时会忽略离群值)。
为了提供良好的用户体验,网站应尽力将 Interaction to Next Paint 控制在 200 毫秒以内。为了让大多数用户都能达到此目标值,一个不错的衡量阈值是按移动设备和桌面设备细分的网页加载时间的第 75 个百分位数。
根据网站的不同,互动可能很少或没有,例如,网页上大多是文字和图片,很少或没有互动元素。或者,对于文本编辑器或游戏等网站,互动次数可能达到数百次甚至数千次。无论哪种情况,如果 INP 较高,用户体验都会受到影响。
虽然改进 INP 需要时间和精力,但回报是更好的用户体验。在本指南中,我们将探讨如何改进 INP。
找出导致 INP 较差的原因
在修复互动缓慢问题之前,您需要先获取数据,以了解网站的 INP 是较差还是需要改进。获得这些信息后,您就可以进入实验室,开始诊断缓慢的互动,并逐步找到解决方案。
在实际应用中查找缓慢的互动
理想情况下,您应先从实地数据着手,开始优化 INP。在最佳情况下,实时用户监控 (RUM) 提供商提供的实测数据不仅会提供网页的 INP 值,还会提供相关数据,突出显示导致 INP 值的具体互动、互动是在网页加载期间还是之后发生的、互动类型(点击、按键或点按)以及其他有价值的信息。
如果您不依赖 RUM 提供商来获取实测数据,INP 实测数据指南建议使用 PageSpeed Insights 查看 Chrome 用户体验报告 (CrUX) 数据,以帮助填补空白。CrUX 是核心网页指标计划的官方数据集,可提供数百万个网站(包括 INP)的指标高级摘要。不过,CrUX 通常不会提供您从 RUM 提供商处获得的背景数据,因此无法帮助您分析问题。因此,我们仍然建议网站尽可能使用 RUM 提供商,或实现自己的 RUM 解决方案,以补充 CrUX 中提供的内容。
在实验室中诊断缓慢的互动
理想情况下,当现场数据表明存在互动缓慢的情况时,您就可以开始在实验室中进行测试了。在没有实地数据的情况下,有一些策略可用于在实验室中识别缓慢的互动。此类策略包括遵循常见用户流程并测试沿途的互动,以及在加载期间(主线程通常最繁忙)与网页互动,以便在用户体验的关键部分识别缓慢的互动。
优化互动
确定缓慢的互动并能在实验室中手动重现该互动后,下一步就是对其进行优化。
互动可分为三个子部分:
- 输入延迟时间,从用户开始与网页互动时算起,到互动事件回调开始运行时结束。
- 处理时长,即事件回调运行到完成所需的时间。
- 呈现延迟时间,即浏览器呈现包含互动视觉效果的下一个帧所用的时间。
这三个子部分的总和就是总互动延迟时间。互动的每个子部分都会为总互动延迟时间贡献一定的时间,因此了解如何优化互动的每个部分,使其尽可能短时间运行非常重要。
识别并缩短输入延迟时间
当用户与网页互动时,该互动的第一个部分是输入延迟时间。根据网页上的其他活动,输入延迟可能会相当长。这可能是由于主线程上发生的活动(可能是由于脚本加载、解析和编译)、提取处理、计时器函数,甚至由于快速连续发生且相互重叠的其他互动所致。
无论互动的输入延迟来自何处,您都需要将输入延迟缩短到最低限度,以便互动能够尽快开始运行事件回调。
启动期间脚本评估与长时间运行任务之间的关系
网页生命周期中交互性的一个关键方面是在启动期间。网页加载时,会先进行渲染,但请务必注意,网页已渲染并不意味着网页已完成加载。根据网页完全正常运行所需的资源数量,用户可能会在网页仍在加载时尝试与网页互动。
在网页加载期间,脚本评估可能会延长互动的输入延迟时间。从网络中提取 JavaScript 文件后,浏览器在运行该 JavaScript 之前仍需执行一些工作;这些工作包括解析脚本以检查其语法是否有效、将其编译为字节码,然后最终执行它。
根据脚本的大小,这项工作可能会在主线程上引入长时间运行的任务,从而延迟浏览器对其他用户互动的响应。为了让网页在加载期间对用户输入保持响应,请务必了解如何减少网页加载期间出现长时间任务的可能性,以便网页保持快速响应。
优化事件回调
输入延迟只是 INP 衡量指标的第一部分。您还需要确保响应用户互动的事件回调能够尽快完成。
经常让出主线程
优化事件回调的最佳一般性建议是尽可能减少其中的工作量。不过,您的互动逻辑可能很复杂,您可能只能略微减少其工作量。
如果您发现自己的网站存在这种情况,接下来可以尝试将事件回调中的工作拆分为单独的任务。这样可以防止集体工作变成阻塞主线程的长任务,从而使原本需要等待主线程的其他互动能够更快地运行。
setTimeout
是一种分解任务的方式,因为传递给它的回调会在新任务中运行。您可以单独使用 setTimeout
,也可以将其使用抽象为单独的函数,以实现更符合人体工程学的生成。
无差别地让出资源比根本不让出资源要好,但还有一种更细致的方式可以向主线程让出资源,即仅在更新用户界面的事件回调之后立即让出资源,以便渲染逻辑能够更快地运行。
让步以允许更早地进行渲染工作
一种更高级的让步技术涉及在事件回调中构建代码,以将运行的内容限制为仅用于为下一帧应用视觉更新所需的逻辑。其他所有内容都可以推迟到后续任务中处理。这不仅能让回调保持轻量级和灵活性,还能通过不允许视觉更新阻塞事件回调代码来缩短互动渲染时间。
例如,假设有一个富文本编辑器,可在您输入文字时设置文字格式,同时还会根据您输入的内容更新界面的其他方面(例如字数统计、突出显示拼写错误和其他重要的视觉反馈)。此外,应用可能还需要保存您撰写的内容,以便您在离开后返回时不会丢失任何工作。
在此示例中,需要根据用户输入的字符执行以下四项操作。不过,只有第一项需要在呈现下一帧之前完成。
- 使用用户输入的文字更新文本框,并应用任何所需的格式。
- 更新显示当前字数的界面部分。
- 运行逻辑以检查拼写错误。
- 保存最近的更改(本地或远程数据库)。
实现此目的的代码可能如下所示:
textBox.addEventListener('input', (inputEvent) => {
// Update the UI immediately, so the changes the user made
// are visible as soon as the next frame is presented.
updateTextBox(inputEvent);
// Use `setTimeout` to defer all other work until at least the next
// frame by queuing a task in a `requestAnimationFrame()` callback.
requestAnimationFrame(() => {
setTimeout(() => {
const text = textBox.textContent;
updateWordCount(text);
checkSpelling(text);
saveChanges(text);
}, 0);
});
});
以下可视化图表显示了将所有非关键更新延迟到下一帧之后可以如何缩短处理时长,从而缩短整体互动延迟时间。