performance.getEntries() 返回空或不全数据是因快照机制有触发条件和生命周期限制:缓冲区容量有限、非实时刷新、条目写入依赖事件(如加载完成)、跨域需配置Timing-Allow-Origin及crossorigin属性、瞬时指标须用PerformanceObserver提前监听、需主动扩大缓冲区并监听bufferfull事件。

直接调用 performance.getEntries() 很容易拿到空数组或不全的数据——这不是 API 失效,而是快照机制本身有明确的触发条件和生命周期限制。
快照不是实时刷新,而是缓冲区当前状态
浏览器对不同性能条目(navigation、resource、paint 等)维护独立的内存缓冲区,默认容量有限(如 resource 类型通常最多存 150–250 条)。getEntries() 返回的是调用时刻缓冲区的静态快照,不会主动“等待”新数据写入。
- 资源加载完成才写入 resource 条目,所以提前调用必然为空
- 首屏绘制事件(FP/FCP)只发生一次,错过注册就无法回溯
- 缓冲区满后,旧条目会被自动丢弃,尤其在 SPA 频繁路由时易丢失早期资源
确保能取到 navigation 和 resource 数据
这两类是最基础的页面加载快照,但必须满足时机和配置要求:
- navigation 条目只在页面首次加载时生成,且必须等
document.readyState === 'complete'或window.addEventListener('load', ...)后获取 - resource 条目默认只包含同源资源;跨域资源(如 CDN 图片、字体)需服务端响应头含
Timing-Allow-Origin: *,否则duration、responseStart等字段为 0 - 给图片、脚本等跨域资源显式添加
crossorigin="anonymous"属性,否则即使服务端配了头,浏览器也不记录细粒度时间
捕获 paint 和 long-task 这类“一过性”指标
FP、FCP、LCP、长任务等不是持续存在的数据,而是瞬时事件。靠轮询 getEntries() 几乎必然漏掉。
- 必须用
PerformanceObserver提前监听,且代码要放在<head>内同步执行(不加defer、不包在函数里) - 监听
paint类型可捕获 FP/FCP;监听largest-contentful-paint单独获取 LCP,取list.getEntries().pop()才是最终值 - 监听
long-task可识别主线程阻塞,但需注意:Chrome 中该类型默认启用,其他浏览器可能需要手动开启
主动扩大缓冲区并防丢数据
默认缓冲区太小,尤其对资源密集型页面,很容易覆盖关键条目。
- 在页面最早期(如
<head>中)调用performance.setResourceTimingBufferSize(500),避免 resource 条目被裁剪 - 监听
bufferfull事件,在缓冲区将满时主动导出并清空:performance.onresourcetimingbufferfull = () => { sendToAnalytics(performance.getEntriesByType('resource')); performance.clearResourceTimings(); } - 对 navigation 和 paint 类型虽无类似接口,但它们数量固定(每页最多一个 navigation,几个 paint),重点在于注册时机不能晚


















