你以为 INP 达标 SPA 就算快了?今天 Chrome 151 把这件事用四个 API 说清楚了

用户点了个按钮,页面换了路由,URL 变了,内容全换了——但 Chrome 告诉你 INP 是绿色的。你会不会觉得哪里不对?

这就是 SPA 性能优化的真实困境:INP 测的是单次交互的响应速度,但 SPA 里每次「跳转」都是 JavaScript 换内容,URL 通过 history.pushState 更新,浏览器从来不认为这是一次新的「页面加载」。结果就是:INP 分数漂漂亮亮,用户却说「点了没反应」。

Chrome 151(2026 年 7 月 28 日稳定版)正式把两个新 entry 类型送进了性能时间线,把这个十年盲区彻底堵上了。

旧方案的局限:三个阶段和一个空白

INP 把一次交互的延迟拆成了三个阶段:输入延迟(主线程被占用)、处理时间(JS 执行)、呈现延迟(样式计算+绘制)。但这套模型只适用于页面内的交互——按钮点击、输入框输入、键盘操作。

SPA 路由跳转不属于上述任何一类。history.pushState() 触发的内容更新,浏览器不建立新的时间原点,后续所有 performance.getEntries() 都还挂在最初那个 URL 上。结果就是:

你永远不知道 /dashboard → /products/123 这个用户点击了多少毫秒才看到内容。INP 不测这个,Lighthouse 不测这个,PageSpeed Insights 也测不了这个。

Chrome 151 的两个新 entry 类型

Chrome 151 向 Performance Timeline 添加了两个新 entry 类型:

1. soft-navigation

当用户触发同文档状态变更(history.pushState/replaceState + URL 更新)时,浏览器发出这个 entry。它做了两件关键的事:

  • 携带 navigationId、name(路由名称)、interactionId(触发这次跳转的交互 ID)
  • 建立新的时间原点。此后所有 first-paint、largest-contentful-paint、event、layout-shift 都归到这个路由下,而不是最初的 document URL

此外,soft-navigation entry 暴露了一个方法:getLargestInteractionContentfulPaint(),直接拿到这次路由的「最大内容绘制」——相当于 SPA 场景下的 LCP。

2. interaction-contentful-paint(ICP)

当一次交互产生了新的内容绘制,且绘制区域正好落在交互修改过的 DOM 范围内时触发。关键特性:它测的是「正确的内容在正确的位置被绘制出来」,而不是「浏览器绘制了下一帧」。即使内容是通过 fetch 异步获取、400ms 后才到达 DOM,也能量到。

这两个 entry 都是 Chrome 151 无条件全量开启的,不需要任何 flag。

四个工具的分工

ICP 不是用来替代 INP 的,它们解决不同层次的问题:

工具 测量内容 粒度 浏览器支持
INP 用户点击到下一帧的总延迟 全页面 Chrome/Safari/Firefox(CWV)
interaction-contentful-paint 交互区域内的内容绘制 单次交互 Chrome 151+
soft-navigation 同文档 URL 状态变更 路由 Chrome 151+
Long Animation Frames(LoAF) 超过 50ms 的动画帧及原因脚本 帧/任务 Chrome/Chromium

实际工作中,Mintec 团队总结出了一套四层诊断流程:

  1. INP 告诉你「有东西慢了」——全局报警
  2. soft-navigation 告诉你「哪条路由慢」——定位到 URL
  3. interaction-contentful-paint 告诉你「哪一次交互导致的渲染慢」——定位到事件
  4. LoAF 告诉你「哪一行代码在作妖」——定位到具体脚本

这四级粒度以前从来没有同时存在过。

生产环境怎么接

不需要任何第三方库,一个 PerformanceObserver 够了:

“`javascript
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === “soft-navigation”) {
// 路由级 LCP 等效
entry.getLargestInteractionContentfulPaint?.().then((icp) => {
console.log(“路由:”, entry.name, “ICP:”, Math.round(icp?.startTime), “ms”);
});
}
if (entry.entryType === “interaction-contentful-paint”) {
console.log(“ICP:”, entry.startTime, “ms, 交互:”, entry.interactionId);
}
};
}).observe({ type: [“soft-navigation”, “interaction-contentful-paint”], buffered: true });
“`

buffered: true 保证页面加载时已有的 entry 不会漏掉,可以直接放在 `<head>` 的内联脚本里,从第一个字节开始收数据。

几个实际注意点

第一,Safari 不支持这两个 entry。 目前只有 Chrome 151+ 支持,Firefox 和 Safari 暂时没有跟进计划。生产环境建议做 feature detection:

“`javascript
if (PerformanceObserver.supportedEntryTypes.includes(“soft-navigation”)) {
// 接入上述 observer
}
“`

不支持的浏览器里,这套 API 是静默降级的,不影响功能。

第二,ICP 不能替代 INP 做 CWV 上报。 INP 仍然是 Google 排名的响应性指标,ICP 是诊断工具,两者配合使用。如果你的 SPA 用的是 Google Search Console 的 CrUX 数据,现在终于可以看到分路由的 breakdown 了。

第三,soft-navigation 依赖浏览器的导航检测。 浏览器对 history.pushState/replaceState 的检测并不完美,有些自定义路由库可能需要额外的 speculation rules 来帮助浏览器识别跳转。

三步下一步

第一步,在 Chrome DevTools Performance 面板里录制一次完整的用户操作路径,看看 soft-navigation entry 出现了几次、ICP 时间分别是多少。这不需要改一行代码,打开 DevTools 就能做。

第二步,把上面的 PerformanceObserver 片段接入你的应用,改成真实的上报逻辑——发给你的 RUM 后台或者 Google Analytics 4 的自定义事件里。

第三步,把 CrUX 报告里按 URL 拆开看。现在 Search Console 已经支持 SPA 路由级数据了,对照刚才的 ICP 数据,就能知道是哪个路由在拖 INP 后腿。

INP 绿色不代表 SPA 快,只是说明那些被测到的单次点击没超时。真正的慢,往往藏在路由跳转里——今天这件事,Chrome 终于帮你看见了。

评论区

0 条评论

登录后可评论。

阿速·性能优化 110 阅读