看得见与能操作是两个不同的时刻
别名: TTI · INP · FCP · LCP · 可交互 · 首次绘制
概念解释
像素出现在屏幕上,和这次出现已经能在时限内响应输入,是两个时钟。首次绘制(first paint,以及后续的 LCP)标记「看得见」;可交互(TTI 所描述的就绪,以及字段上的 INP)标记「能操作」。中间这段空隙里,按钮已经画成最终样子,主线程却可能还在跑脚本、水合还没把监听绑上,点击会丢。看见首屏不能代替「能点了」。
它处理的是这两个时刻为什么不是同一个探针,不是性能预算该绑哪类设备,也不是预算缺了就不会有人跑。
机制
绘制只要求布局、绘制树和这一帧的像素。事件处理要求主线程空闲到能走完监听、更新状态、再画下一帧。现代前端把首屏 HTML 先画出来(看得见),把行为放在稍后到达的 JavaScript 里(能操作更晚)。水合把已有 DOM 重新绑上框架事件,完成之前的点击被丢掉或排进未就绪的队列。TTI 试图标「主线程持续空闲、可以稳定交互」的点;INP 量的是真实点击/敲键/轻点从输入到下一帧的延迟。两者都不是绘制时间戳。LCP 大图出来的那一瞬,INP 仍可能很差——用户看见了最大元素,点下去却要等数百毫秒。
所以「首屏」若只报绘制,优化会被吸去压图片和字体,脚本仍然堵住输入。两个时刻用不同的探针,是因为它们对应流水线的不同阶段:像素 vs 输入处理。把其中一个当另一个用,会在错误的阶段停工。
边界
几乎无脚本的静态文档,两个时刻可以贴得很近,分开报的收益小,但仍不是同一个量。游戏循环、canvas、原生客户端不走网页的 TTI/INP 定义,要换它们自己的「第一帧」和「第一口输入」。预渲染、bfcache 恢复会让绘制极早,可交互取决于恢复后的事件是否还在。只看实验室里的 TTI 会漏掉字段里偶发的长任务;只看 INP 又不能回答「用户第几秒才第一次能点」。视觉上已禁用的控件(灰掉、骨架)是故意让两个时刻对齐的手段,不属于「看得见等于能操作」的误用。
怎么落地
- 把「看得见」和「能操作」拆成两条目标:绘制用 LCP / first paint,交互用 INP(字段)以及实验室里的 TTI 或 TBT,不要用一个首屏数字覆盖。
- 主路径上的可点控件,在监听绑好之前不要画成最终可点外观;骨架或禁用态用来对齐两个时刻。
- 水合完成前丢弃或明确排队点击,并在就绪后给一次可见反馈,避免空隙里的点击消失。
- 验证:慢 3G 加 CPU 限速,录屏并对齐点击。记下像素出现的时间和第一次点击得到响应的时间,两格都要报。若绘制已绿、点击仍要等,说明只优化了看得见。