PerformanceObserver 监听不到首次 paint 事件怎么办?

司空奕卓 阅读 33

我在用 PerformanceObserver 监控 FP 和 FCP,但页面加载后 observer 回调根本没触发,是不是我写法有问题?

已经确认浏览器支持,也把 entryTypes 写对了,但就是收不到首次渲染的事件。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.log('Paint:', entry.name, entry.startTime);
  });
});
observer.observe({ entryTypes: ['paint'] });
我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
FSD-苗苗
问题在于你创建 observer 的时机太晚了,paint 事件在页面加载早期就触发了,等你 observer 起来的时候 first-paint 和 first-contentful-paint 已经发完了。

两种解决思路:

第一种,页面早期就创建 observer,把 script 放到 head 里越早越好:

<script>
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.log('Paint:', entry.name, entry.startTime);
});
});
observer.observe({ entryTypes: ['paint'] });
</script>


第二种,兼容已经发生的 paint,用 getEntriesByType 兜底:

const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.log('Paint:', entry.name, entry.startTime);
});
});
observer.observe({ entryTypes: ['paint'] });

// 兜底:获取已经发生的 paint 事件
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach(entry => {
console.log('Paint (cached):', entry.name, entry.startTime);
});


实际项目里两种一起用最稳妥,兜底方案能保证不丢数据。
点赞 1
2026-03-12 19:07
Good“乐萱
这其实是个典型的竞态问题,不是写法错,而是时机不对。FP(First Paint)和 FCP(First Contentful Paint)发生得非常早,通常在页面解析刚开始的时候。如果你的脚本放在 body 底部,或者是异步加载的,等你执行到 new PerformanceObserver 的时候,浏览器早就画完第一帧了,事件早就过去了,观察者当然抓不到。

要优雅地解决这个问题,必须把这段监听代码放到 head 标签的最顶部,确保在 CSS 加载和页面渲染之前就执行。这样你才能在事件发生前就埋伏好。

正确的代码位置应该像下面这样:

<!DOCTYPE html>
<html>
<head>
<!-- 关键点:必须放在最前面,要在 CSS 之前 -->
<script>
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.log('Paint:', entry.name, entry.startTime);
});
});
observer.observe({ entryTypes: ['paint'] });
</script>

<link rel="stylesheet" href="style.css">
</head>
<body>
...
</body>
</html>


这样改完就能正常触发了。顺便说一句,虽然现在的浏览器大多支持,但为了代码的健壮性,建议加个 if ('PerformanceObserver' in window) 的判断,这样代码看起来更严谨,也更优雅。
点赞 2
2026-03-04 10:08