先看现象。实验室里 TBT 只有 120ms,按经验 INP 应该在 200ms 以内,但真实用户数据里 INP 到了 520ms。Performance 面板看主线程大部分时间是空闲的,找不到明显超过 50ms 的长任务,点击到画面更新之间却明显卡顿。
把日志和配置对完,能解释这个现象的基本就这几处。慢在事件处理之后的分帧渲染 不属于长任务统计范围。另一种情况是第三方脚本用 requestIdleCallback 抢占了交互后的空闲时间。
少数情况下是点击触发的请求要走一次冷启动 服务端首字节就有 300ms。
不容易想到的是,框架的批处理把更新推迟到下一帧 中间还插了一次动画。
动手时先按交互类型拆开看哪类操作最慢
new PerformanceObserver(l => l.getEntries().forEach(e => console.log(e.name, e.duration, e.interactionId))).observe({type:'event', buffered:true});
然后记录点击到请求返回的耗时
curl -s -o /dev/null -w 'dns %{time_namelookup} ttfb %{time_starttransfer}\n' https://www.example.com/api/search
接着把非关键脚本挪出交互后的时间段
window.addEventListener('click', () => requestIdleCallback(fn), {passive: true});
给交互后的更新加一层占位反馈
btn.addEventListener('click', () => { btn.classList.add('is-loading'); fetch(url).then(render); });
改完先别撤,确认这几项。把接口改成预热连接并给按钮加即时反馈后,同类交互的 INP 从 520ms 降到 210ms,TBT 基本没变,说明瓶颈确实不在长任务。
提醒一点。INP 统计的是整个交互链路,长任务只覆盖其中一段,接口耗时的延迟渲染同样会被算进去。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。
