必须用 PerformanceObserver 监控 LCP,因 LCP 动态演进,getEntriesByType 仅捕获调用时刻已定格条目,无法监听后续变化;PerformanceObserver 事件驱动且需设 buffered: true 才能捕获初始化前条目。

直接用 PerformanceObserver 监控 largest-contentful-paint 类型条目,是当前唯一可靠方式;performance.getEntriesByType('largest-contentful-paint') 已废弃,主动调用会返回空数组。
为什么必须用 PerformanceObserver 而不是 getEntriesByType
LCP 是动态演进的:页面加载过程中,最大内容元素可能多次变更(比如 banner 图还没加载完,先渲染了标题文本;等图片加载完成,LCP 就切换到该图片)。getEntriesByType 只能捕获调用时刻已“定格”的条目,无法监听后续变化。而 PerformanceObserver 是事件驱动,能持续接收新条目,确保捕获最终 LCP 值。
-
buffered: true必须设,否则初始化前的 LCP 条目(如首屏图)会被丢弃 - 若在
中延迟加载监控脚本(比如包裹在DOMContentLoaded里),大概率错过首次 LCP 记录 - 跨域 iframe 内的 LCP 不会上报到主页面,需在 iframe 内单独注入 observer
如何正确获取 LCP 时间值
LCP 条目的 startTime 字段不可靠;应优先取 renderTime, fallback 到 loadTime(对图片/视频)或 startTime(对文本节点)。
- 现代浏览器中,
renderTime表示内容实际绘制完成的时间点,最贴近用户感知 -
loadTime仅对资源加载类元素(、)有效,但可能早于渲染(比如图片下载完但尚未解码/布局) - 不要直接用
entry.startTime—— 它在预渲染页(如 Chrome 的 prerendering)中未对齐激活时间基准
示例代码:
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
const lcpTime = lastEntry.renderTime || lastEntry.loadTime || lastEntry.startTime;
console.log('LCP:', lcpTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
什么时候该停止 LCP 监控
用户开始交互后,LCP 就不再反映“初始加载体验”,继续监听可能误报(比如点击触发懒加载大图,导致新 LCP 条目出现)。
- 推荐在
click或keydown首次触发时调用observer.disconnect() - 避免监听
scroll—— 滚动本身不构成用户意图输入,且易被自动滚动(如锚点跳转)触发 - 如果页面有强交互引导(如弹窗、表单聚焦),可结合
focusin事件补充断开逻辑
用 web-vitals 库是否更省事
是,尤其当你需要兼容预渲染、跨域 iframe、SPA 路由切换等场景时。web-vitals 自动处理了时间基准修正(比如用 navigationStart 还是 activationStart)、候选元素过滤、重复上报抑制。
- 它内部仍是封装
PerformanceObserver,但屏蔽了大量边界 case - 不依赖全局
performance对象状态,适合模块化打包(如通过import { getLCP } from 'web-vitals') - 注意:v3+ 版本默认只上报“稳定后”的 LCP(即用户无交互 1s 后),若需首屏立即上报,要传
{ reportAllChanges: true }
真正容易被忽略的是:LCP 候选元素必须在初始 HTML 中可被预扫描器发现。JS 动态插入、data-src 替换、loading="lazy" 都会让关键图像“隐身”,导致 LCP 值虚高——这和监控代码本身无关,得回 HTML 源头改。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











