过去,衡量网页主要内容的加载速度和向用户显示速度一直是 Web 开发者的一大难题。load 或 DOMContentLoaded 等较旧的指标效果不佳,因为它们与用户在屏幕上看到的内容不一定对应。以用户为中心的较新性能指标(如 First Contentful Paint (FCP))只能捕获加载体验的初始阶段。如果网页显示启动画面或加载指示器,这一时刻与用户不太相关。
过去,我们推荐过首次有效绘制 (FMP) 和速度指数 (SI)(均在 Lighthouse 中提供)等性能指标,以帮助捕获初始绘制后的更多加载体验,但这些指标复杂、难以解释且经常出错 - 这意味着它们仍然无法确定网页的主要内容何时加载完毕。
根据 W3C 网站性能工作组的讨论和 Google 开展的研究,我们发现,衡量网页主要内容的加载时间时,更准确的方法是查看渲染最大的元素的时间。
什么是 LCP?
LCP 会报告视口中可见的最大图片、文本块或视频的渲染时间(相对于用户首次导航到网页的时间)。
LCP 得分多少算好?
为了提供良好的用户体验,网站应尽量将 Largest Contentful Paint 控制在 2.5 秒或更短的时间内。为确保大多数用户都能达到此目标值,一个合适的衡量阈值是网页加载时间的第 75 个百分位数,并按移动设备和桌面设备进行细分。
我们会考虑哪些元素?
如 Largest Contentful Paint API 中当前所指定,考虑最大内容渲染时间的元素类型包括:
<img>
元素(第一帧呈现时间适用于 GIF 或动画 PNG 等动画内容)<svg>
元素中的<image>
元素<video>
元素(使用海报图片加载时间或视频的第一帧呈现时间,以时间较短者为准)- 使用
url()
函数加载背景图片的元素(而不是 CSS 渐变) - 包含文本节点或其他内嵌级文本元素子级的块级元素。
请注意,将元素限制到这个有限的集合是有意为之,以便一开始就简单易行。随着我们开展更多研究,未来可能会添加其他元素(例如完整的 <svg>
支持)。
除了仅考虑某些元素之外,LCP 衡量结果还会使用启发词语来排除用户可能会视为“无内容”的某些元素。对于基于 Chromium 的浏览器,这些包括:
- 不透明度为 0 且对用户不可见的元素
- 覆盖整个视口的元素,此类元素可能会被视为背景(而非内容)
- 占位图片或其他熵值较低的图片,可能无法反映网页的真实内容
浏览器可能会继续改进这些启发词语,以确保我们能满足用户对最大内容丰富元素的预期。
这些“内容”启发词语可能与 First Contentful Paint (FCP) 使用的启发词语不同,后者可能会考虑其中一些元素(例如占位符图片或整个视口图片),即使它们不符合 LCP 候选条件也是如此。虽然这两个指标的名称中都使用了“内容丰富”一词,但它们的目标不同。FCP 会衡量何时将任何内容绘制到屏幕上,以及 LCP 何时绘制到主内容,以便提高 LCP 的选择性。
元素大小是如何确定的?
针对 LCP 报告的元素的尺寸通常是用户在视口内可见的尺寸。如果元素延伸到视口之外,或者任何元素被截断或具有不可见的“溢出”,则这些部分不会计入元素的尺寸。
对于已从固有大小调整大小的图片元素,系统会报告较小的可见大小或固有大小。
对于文本元素,LCP 仅考虑可包含所有文本节点的最小矩形。
对于所有元素,LCP 均不会考虑使用 CSS 应用的外边距、内边距或边框。
何时报告 LCP?
网页通常会分阶段加载,因此,网页上最大的元素可能会发生变化。
为了处理这种潜在的变化,浏览器会在绘制第一帧后立即调度类型为 largest-contentful-paint
的 PerformanceEntry
,以标识最大的内容元素。但是,在渲染后续帧后,每当 Largest Contentful Element 发生变化时,它都会调度另一个 PerformanceEntry
。
例如,在包含文本和主推图片的页面上,浏览器可能最初只会渲染文本,此时浏览器会分派一个 largest-contentful-paint
条目,其 element
属性可能会引用 <p>
或 <h1>
。稍后,当主打图片完成加载后,系统会分派第二个 largest-contentful-paint
条目,并且其 element
属性将引用 <img>
。
元素只有在已呈现且对用户可见后,才会被视为最大的内容元素。尚未加载的图片不计入“呈现”次数。在字体阻止期内,使用网页字体的文本节点也不例外。在这种情况下,系统可能会报告较小的元素为最大内容渲染时间元素,但一旦较大的元素完成渲染,系统就会创建另一个 PerformanceEntry
。
除了延迟加载图片和字体之外,当有新内容可用时,页面可能还会向 DOM 添加新元素。如果其中任何新元素都大于之前最大的有内容元素,系统也会报告新的 PerformanceEntry
。
如果从视口中移除(甚至从 DOM 中移除)最大的内容元素,除非渲染出更大的元素,否则它仍然是最大的内容元素。
一旦用户与网页互动(通过点按、滚动或按键操作),浏览器就会停止报告新条目,因为用户互动通常会更改对用户可见的内容(这在滚动时尤其如此)。
出于分析目的,您应仅向分析服务报告最近调度到的 PerformanceEntry
。
加载时间与呈现时间
出于安全考虑,对于缺少 Timing-Allow-Origin
标头的跨源图片,系统不会公开图片的呈现时间戳。而是仅公开其加载时间(因为此信息已通过许多其他 Web API 公开)。
这可能会导致 Web API 报告的 LCP 时间早于 FCP,这似乎是不可能的。事实并非如此,而只是由于这种安全限制而出现。
我们始终建议您尽可能设置 Timing-Allow-Origin
标头,以便获得更准确的指标。
如何处理元素布局和大小更改?
为了降低计算和调度新性能条目的性能开销,对元素的大小或位置所做的更改不会生成新的 LCP 候选项。系统只会考虑元素的初始尺寸和在视口中的位置。
这意味着,最初在屏幕外呈现,然后转换到屏幕上的图片可能不会被报告。这也意味着元素最初在视口中渲染,然后被下推,超出视图仍将报告其初始的视口内尺寸。
示例
以下是一些示例,展示了在一些热门网站上发生 Largest Contentful Paint 的时间:
在上述两个时间轴中,最大的元素会随着内容的加载而发生变化。在第一个示例中,新内容会添加到 DOM 中,这会更改最大的元素。在第二个示例中,布局发生了变化,并且之前最大的内容已从视口中移除。
虽然延迟加载的内容大于网页上已有的内容,但实际情况并非如此。接下来的两个示例展示了 LCP 在页面完全加载之前发生的情况。
在第一个示例中,Instagram 徽标会相对较早加载,即使其他内容逐渐显示,它仍会是最大的元素。在 Google 搜索结果页示例中,最大的元素是显示在任何图片或徽标完成加载之前的一段文本。由于所有单独的图片都小于此段落,因此在整个加载过程中,它始终是最大的元素。
如何衡量 LCP
实地工具
实验室工具
在 JavaScript 中衡量 LCP
如需在 JavaScript 中衡量 LCP,您可以使用 Largest Contentful Paint API。以下示例展示了如何创建一个 PerformanceObserver
,用于监听 largest-contentful-paint
条目并将其记录到控制台。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
在上面的示例中,每个记录的 largest-contentful-paint
条目都代表当前的 LCP 候选项。通常,发出的最后一个条目的 startTime
值是 LCP 值,但并非总是如此。并非所有 largest-contentful-paint
条目都适用于衡量 LCP。
以下部分列出了 API 报告的内容与指标计算方式之间的差异。
指标与 API 之间的差异
- 该 API 会为在后台标签页中加载的网页分派
largest-contentful-paint
条目,但在计算 LCP 时应忽略这些网页。 - 网页进入后台后,该 API 将继续调度
largest-contentful-paint
条目,但在计算 LCP 时应忽略这些条目(仅当网页在整个时间都处于前台时,才能考虑元素)。 - 当网页从返回/前进缓存恢复时,API 不会报告
largest-contentful-paint
条目,但在这些情况下,应衡量 LCP,因为用户会将其视为不同的网页访问。 - API 不会考虑 iframe 中的元素,但该指标会考虑它们,因为它们是网页用户体验的一部分。在 iframe 中包含 LCP 的网页(例如嵌入式视频的海报图片)中,这将显示为 CrUX 和 RUM 之间的差异。为了准确衡量 LCP,您应考虑这些因素。子帧可以使用该 API 将其
largest-contentful-paint
条目报告给父帧以进行汇总。 - 该 API 会从导航开始时刻开始衡量 LCP,但对于预渲染网页,应从
activationStart
开始衡量 LCP,因为这与用户体验到的 LCP 时间相对应。
开发者可以使用 web-vitals
JavaScript 库来衡量 LCP,而无需记住所有这些细微差异,该库会为您处理这些差异(在可能的情况下,请注意不涵盖 iframe 问题):
import {onLCP} from 'web-vitals';
// Measure and log LCP as soon as it's available.
onLCP(console.log);
如需查看有关如何在 JavaScript 中衡量 LCP 的完整示例,请参阅 onLCP()
的源代码。
如果最大的元素不是最重要的元素,该怎么办?
在某些情况下,网页上最重要的元素与最大的元素并不相同,开发者可能更感兴趣衡量这些其他元素的呈现时间。您可以使用 Element Timing API 实现此目的,如自定义指标一文中所述。
如何提高 LCP
我们提供了有关优化 LCP 的完整指南,可引导您完成在现场识别 LCP 时间并使用实验室数据进行深入分析和优化的流程。
其他资源
- Annie Sullivan 在 performance.now() 大会 (2019) 上发表的演讲“从 Chrome 性能监控中总结出的经验”
更新日志
有时,错误是在用于衡量指标的 API 中发现的,有时是在指标本身的定义中发现的。因此,有时必须进行更改,这些更改可能会在内部报告和信息中心中显示为改进或回归。
为帮助您管理这些指标,我们会在此更新日志中显示对这些指标的实现或定义所做的所有更改。
如果您对这些指标有任何反馈,可以通过 web-vitals-feedback Google 群组提供反馈。