过去,衡量网页主要内容的加载速度和向用户显示速度一直是 Web 开发者的一大难题。load 或 DOMContentLoaded 等旧版指标效果不佳,因为它们不一定与用户在屏幕上看到的内容相对应。而首次内容渲染 (FCP) 等以用户为中心的新型性能指标仅捕获加载体验的开头部分。如果网页显示启动画面或显示加载指示器,此时刻与用户的相关性不高。
过去,我们曾建议使用 First Meaningful Paint (FMP) 和 Speed Index (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 中发现 bug,有时也会在指标本身的定义中发现 bug。因此,有时必须进行更改,这些更改可能会在内部报告和信息中心中显示为改进或回归。
为帮助您管理这些指标,我们会在此更新日志中显示对这些指标的实现或定义所做的所有更改。
如果您对这些指标有任何反馈,可以通过 web-vitals-feedback Google 群组提供反馈。