interaction to next paint 怎么优化:先量出最慢的那次交互,再拆延迟
interaction to next paint(INP)量的是页面响应一次点击、轻触或按键要多久,合格线是 75 分位的 200 毫秒以内。这一章教你先找出最慢的那次交互,再拆开延迟、只修占大头的那一块。

interaction to next paint(INP)量的是用户点一下、tap 一下或按下键盘之后,页面要多久才给出视觉反馈,合格线是 75 分位的 200 毫秒以内。Google 对它的官方定义是:观察「用户对页面做过的所有交互的延迟」,最后报出一个「所有或几乎所有交互都没有超过」的单值(web.dev,2026-09-30 访问)。一次慢交互就能毁掉整天的读数,而慢的地方基本都在你自己上线的那段 JavaScript 里。
读这篇前
你只需要一个能在浏览器里打开的页面,外加一个能拿到真实用户数据的地方。这一章只讲响应速度。同一份报告里还有加载那一半,如果你卡在「最大的那块内容半天不出现」,那是 largest contentful paint 的活;如果你还在纠结这些到底值不值得做,先看 core web vitals 值不值得做。这颗指标在真实首页上长什么样、阻塞时间有多长,数据那一半在 24 个首页的 total blocking time 实测里。
interaction to next paint 到底在量哪一段
它量的是「用户动手」到「浏览器画出下一帧」这一整段,而不是一个能在一个地方压短的数字。这段由三块拼起来,每一块坏掉的原因都不一样。动任何代码之前先把这三块拆开,就是整章方法;改错块,总量一动都不动。
| 组成 | 覆盖哪一段 | 常见成因 |
|---|---|---|
| 输入延迟 | 从用户输入到事件处理函数开始跑 | 主线程上已经有长任务在跑 |
| 处理时长 | 事件处理函数从开始到跑完 | 回调里那段 JavaScript 太重 |
| 呈现延迟 | 处理函数结束到下一帧画出来 | 随后要做的布局、样式与渲染 |
它只算点击、轻触和键盘按键,不算滚动、悬停和缩放。它报的是最差的那次交互,而不是平均,但有一个修正:交互很多的页面上,每 50 次里最高的那一次会被丢掉再取最终值,免得一个偶然的抖动把整页的分数带偏。对大多数页面、尤其对小站来说,最终报出来的就是那一次最差的交互。
这颗指标的观测器有几条真实限制,大部分「测出来的数对不上」都出在这里。时长低于 104 毫秒的事件默认根本不会上报,所以还要把 first-input 一起观察,否则一个很快的页面可能一条都不报。页面从前进后退缓存里恢复时会重置取值,因为用户把它当成一次全新的访问。iframe 里的交互会被指标算进去,却拿不到页面 JavaScript 里,这是官方列出的「脚本和 field 数据对不上」的原因之一。
interaction to next paint 不是你页面加载花了多久,而是用户记得住的那一次交互,你多久才回应。
为什么合格线是 200 毫秒,而不是 0
没有所谓「瞬间响应」,所以这条线是经验值。Google 把合格定在 200 毫秒及以内,取页面访问的 75 分位,手机和桌面分开算。也就是说,四分之一访问可能比你读到的那个数更慢,这是故意的:分位数代表的是大多数访客实际体验到的水平,或更好。
| 档位 | INP 取值 |
|---|---|
| 合格 | 200 毫秒及以内 |
| 需要改善 | 超过 200 毫秒、到 500 毫秒 |
| 差 | 超过 500 毫秒 |
INP 取代的是 First Input Delay:后者只量第一次交互被处理前的那段延迟,前者把输入延迟、完整处理时长和等下一帧的时间全算进去,而且是每一次交互,不只是第一次。标准更严,所以一个当年过了 FID 的页面,今天照样可能在这里挂掉。
按这个顺序做
四步,每步有一个可以验证的完成标志,过了再走下一步。
- 先读 field 数,不要先读 lab 数。 这颗指标是按真实访客定义的。完成标志:你能分别说出手机和桌面的 75 分位 INP。
- 找出最慢的那次交互。 在你能写出它的元素、类型和毫秒值之前,别急着猜原因。完成标志:这三样都写下来了。
- 把它拆成输入延迟、处理时长、呈现延迟。 完成标志:你能说出三块里哪一块占大头,并指到对应的代码。
- 只修占大头的那块,再复测同一次交互。 完成标志:那次交互降下来了,field 数跟着动。
交付物:一段采集代码、一张阈值表、一张修法对照
采集这颗指标,别自己写百分位算法。web-vitals 这个库替你处理了分位、前进后退缓存和后台标签页这几种边界情况,报出来的数和 field 工具是同一个。把它贴进页面,或者接进你现有的埋点。
import {onINP} from 'web-vitals';
onINP(({value, element, attribution}) => {
console.log('INP', value, element, attribution);
});
想看到每一次交互、而不只是最终那个数,就把 Event Timing 观察器的门槛显式写出来,它的下限是 16 毫秒。
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 200) {
console.log(entry.duration, entry.name, entry.target);
}
}
}).observe({type: 'event', buffered: true, durationThreshold: 16});
知道哪一块占大头之后,修法基本落在这三种里;改错块是「改了但数没动」最常见的原因。
| 延迟位置 | 修法 |
|---|---|
| 输入延迟 | 拆开长任务;把加载时就跑的脚本推迟或砍掉 |
| 处理时长 | 把重活挪出回调;DOM 读和写分批做 |
| 呈现延迟 | 缩小布局范围;别做大段同步样式计算 |
两条规则承担了大头。任何单个任务的时长压在 50 毫秒以内,过了这条线浏览器就没法让位给输入。以及别拿自己的笔记本、连着自己的网去测,这颗指标由设备决定的程度远大于由网络决定。
我们测了什么,它证明不了什么
INP 没有真实用户就产不出来,lab 工具能给的最接近的东西是 total blocking time。我们跑了这个:2026-09-30,Lighthouse 13.5.0,mobile,跑的是我们自己的首页,querywin.com 的 TBT 是 2,366 毫秒。它不是 INP,这一章也不会把它当成 INP 来写。它只说明主线程在忙,不说明某一次轻触慢了,因为一次没人在上面操作的 lab 运行,根本产不出一次可计时的交互。这颗指标定义在 field 数上,而这一次我们没能把自家的 field INP 取出来。TBT 低会提高 INP 低的概率,但不保证。
三种会做错的情况
前两种白忙一个迭代,第三种会让团队误以为这颗指标本身坏了,其实是测法坏了。
- 优化加载,没优化交互。 首屏画得快,救不了一个跑 300 毫秒的处理函数。动打包之前先把交互测出来。
- 拿 lab 数当 field 指标的结论。 TBT 可能报一个用户根本撞不上的问题,也可能漏掉一个只有真人边加载边点击时才出现的问题。它用来复现,不用来下结论。
- 改错那一块。 重写处理函数,对「点击之前就已经在跑的长任务」造成的延迟毫无帮助。先拆开,否则力气用错地方。
常见问题
interaction to next paint 是排名因素吗
它是 Core Web Vitals 的一部分,Google 把它列为众多输入之一,我们不会把它说得比这更重。值得修它的诚实理由是:200 毫秒内给出回应的页面,能留住更多真正进来的人。
多少算好
访问的 75 分位在 200 毫秒及以内,手机和桌面分开算。超过 500 毫秒算差,中间那段是需要改善。
为什么要和我测的 TBT 不一样
它们量的是两回事。阻塞时间把加载期间所有长任务加起来,不需要任何交互;interaction to next paint 需要一次真实交互,并且只报最差的那次。阻塞时间是 lab 代理指标,不是替代品。
桌面端要管吗
要,但两边分开算分,一个在快桌面上过关的页面,在中等手机上照样可能不合格。这颗指标受设备影响的程度远大于带宽。
下一步
响应速度只是页面体验的三块之一,而且往往是团队最后才测的那块。这一章如果只带走一件事,就带走这个循环:先读 field 数、找出最慢的交互、拆成三块、修最大的那块、再测一次。在访客真正落地的页面上跑这个循环,正是 QueryWin 在做的事之一。
本文属于 QueryWin 实操手册 · 第 2 阶


