如何用 firstInterimResponseStart 衡量 Early Hints 的真实收益?
题目与背景
浏览器开始支持 PerformanceResourceTiming.firstInterimResponseStart,它记录收到首个临时 1xx 响应字节的时间。请实现观测逻辑,评估 103 Early Hints 是否缩短资源准备时间,并处理不支持、跨源和缓存场景。
面试官考察什么
- 是否理解
requestStart、firstInterimResponseStart与最终响应头时间的关系。 - 是否知道 0 既可能表示没有 1xx,也可能来自跨源时序遮蔽或缓存。
- 是否能用
PerformanceObserver处理 buffered 条目和上报采样。 - 是否能把协议收益与浏览器兼容、Timing-Allow-Origin 和业务指标分开。
先问清楚的澄清问题
观测目标
要衡量的是收到 1xx 的等待时间、Early Hints 触发的预加载数量,还是最终 LCP、首屏可交互时间?
资源范围
只观测同源主导航,还是包含 CDN、字体和跨源脚本?跨源响应是否配置 Timing-Allow-Origin?
兼容性策略
数据是否用于实时决策?旧浏览器和不支持该属性的浏览器是否必须提供同等降级指标?
30 秒回答框架
我会用 PerformanceObserver 读取资源条目,在属性存在且值大于零时计算 firstInterimResponseStart - requestStart。零值不能直接判定服务器没发 103,因为跨源时序可能被遮蔽,缓存也会给出零。观测系统要记录支持能力、资源同源性和最终响应时间,并用 LCP 等用户指标验证 Early Hints 是否真的带来收益。
深入解答步骤
1. 说明时间点语义
requestStart 表示浏览器即将发出资源请求的时间;firstInterimResponseStart 表示收到首个 1xx 响应的首字节时刻;finalResponseHeadersStart 表示最终响应头到达。若存在临时响应,firstInterimResponseStart - requestStart 可近似首个 1xx 的网络等待。
2. 写出观测器
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const interim = entry.firstInterimResponseStart;
if (typeof interim !== "number" || interim <= 0) continue;
reportTiming({
name: entry.name,
interimWait: interim - entry.requestStart,
finalHeaders: entry.finalResponseHeadersStart - interim,
initiator: entry.initiatorType,
});
}
});
observer.observe({ type: "resource", buffered: true });生产代码应先做属性检测,并限制资源名称、采样率和上报字段,避免把完整 URL 或敏感查询参数发送到分析系统。
3. 解释零值
值为 0 可能表示没有临时响应,也可能表示跨源资源没有通过 Timing-Allow-Origin 暴露时序;缓存命中和取消请求也可能使相关时刻为 0。因此仪表盘应把“未观测到”与“确认没有 1xx”分开,不要用零值计算负数或直接标记失败。
4. 处理跨源资源
若 CDN 或字体来自其他源,服务端需要对允许的站点返回 Timing-Allow-Origin,浏览器才会暴露受保护的计时字段。该响应头必须按实际部署的源配置,不能为所有来源开放;同时仍要接受部分用户因策略不同而不可观测。
5. 区分 103 与最终收益
该属性告诉你收到某个 1xx 的时间,不直接证明它是 103,也不证明预加载资源被使用。结合导航请求、服务器日志或受控实验确认 103,再比较 finalResponseHeadersStart、资源 responseEnd 和 LCP。若预加载过早或命中错误资源,首个 1xx 变快也可能让性能变差。
6. 设计兼容降级
不支持属性的浏览器仍可上报 requestStart、responseStart 和 responseEnd 等基础指标,但不能伪造 interim 时间。用能力字段标记浏览器是否支持该属性,并将新旧样本分开聚合;不要因为 API 缺失就阻断页面渲染。
7. 建立验证和治理
在 HTTP/2 或更高协议、同源和跨源、缓存命中与未命中、服务器发送与不发送 103 的环境中做对照。检查 interimWait >= 0、最终头时间不早于首个临时响应,并限制上报频率。指标解释要同时查看 LCP、预加载命中率和错误率,避免把网络时钟当作业务结果。
高质量示例回答
我会用 buffered PerformanceObserver 读取 firstInterimResponseStart,只在属性存在且非零时报告 1xx 等待时间。零值保留为不可判定状态,并结合同源性、Timing-Allow-Origin、缓存和浏览器能力做分层。是否采用 Early Hints 要由受控实验中的 LCP、资源命中率和错误率决定,而不是只看首个临时响应更早。
常见错误
- 把
firstInterimResponseStart非零当成一定收到 103。 - 把零值一律解释为服务器没有发 1xx。
- 忽略跨源
Timing-Allow-Origin导致的数据缺口。 - 用
responseStart代替最终响应头时间,混淆 1xx 与最终响应。 - 只在页面加载后调用
getEntriesByType,漏掉创建观察器前的条目。 - 只看网络时序,不验证 LCP、预加载命中和错误率。
追问与回答
firstInterimResponseStart 能确认是 103 吗?
不能。它记录首个 1xx 响应字节,可能是 100 Continue 或其他临时响应。要确认 103,应结合服务器日志、受控请求和资源加载行为。
为什么跨源资源经常是零?
Resource Timing 会遮蔽跨源时序;没有匹配的 Timing-Allow-Origin 时,相关字段返回零。应修正响应策略或把样本归为不可观测,而不是补写估算值。
responseStart 和 finalResponseHeadersStart 有何区别?
存在临时响应时,responseStart 可能反映首个临时字节;finalResponseHeadersStart 用于最终响应头时间。评估服务器准备时间应优先使用后者。
旧浏览器如何降级?
做能力检测,继续采集通用的请求和响应时间,并用字段标记缺少 interim 指标。旧浏览器样本应与新 API 样本分开聚合。
如何证明 Early Hints 值得保留?
在相同网络、缓存和资源版本下做启用与禁用对照,比较 LCP、预加载命中率、最终头时间、资源完成时间和错误率;首个 1xx 更早只是中间证据。