你了解你的前端页面性能吗?—— LCP

895 阅读12分钟

注意:Largest Contentful Paint (LCP) 是一项稳定的重要核心网页指标,用于衡量感知到的加载速度,因为它标记了网页加载时间轴中页面主要内容可能已加载的时间点,快速 LCP 有助于让用户确信相应页面很实用

长期以来,衡量网页主要内容的加载速度以及对用户可见是一个一直以来的挑战。load 或 DOMContentLoaded 等旧指标效果不佳,因为它们不一定与用户在屏幕上看到的内容相对应。而较新的以用户为中心的性能指标(例如 First Contentful Paint (FCP))仅捕获加载体验的开始阶段。如果网页显示启动画面或显示加载指示器,则这一时刻与用户没有太大关系。

过去,我们会推荐首次有效渲染时间 (FMP) 和速度指数 (SI)(两者均在 Lighthouse 中提供)等性能指标,以帮助捕捉更多初始绘制后的加载体验,但这些指标比较复杂,难以解释,而且往往是错误的,也就是说,它们仍然无法确定网页主要内容的加载时间。

根据 W3C Web 性能工作组中的讨论和 Google 的研究,我们发现,衡量页面主要内容的加载时间的更准确方法是查看最大的元素的呈现时间。

什么是 LCP?

LCP 报告的是视口中可见最大图片或文本块相对于用户首次导航到网页的呈现时间。

要点:请务必注意,LCP 包含从上一个网页开始的所有卸载时间、连接设置时间、重定向时间和首字节时间 (TTFB)。在现场测量时,这些时间很重要,可能会导致实测测量结果和实验室测量结果之间出现差异。

良好的 LCP 得分是多少?

为了提供良好的用户体验,网站应努力将 Largest Contentful Paint 控制在 2.5 秒以内。为确保您的大多数用户都达到此目标,最好衡量一下网页加载的第 75 个百分位(按移动设备和桌面设备细分)。

良好 LCP 值为 2.5 秒或更短,不良值大于 4.0 秒,两者之间的任何值都需要改进

良好的 LCP 值为 2.5 秒或更短。

良好的 LCP 得分是多少?

为了提供良好的用户体验,网站应努力将 Largest Contentful Paint 控制在 2.5 秒以内。为确保您的大多数用户都达到此目标,最好衡量一下网页加载的第 75 个百分位(按移动设备和桌面设备细分)。

注意:如需详细了解此建议背后的研究和方法,请参阅:定义核心网页指标阈值

需要考虑哪些元素?

根据 Largest Contentful Paint API 中当前的规定,Largest Contentful Paint 考虑的元素类型包括:

  • <img> 元素(第一帧呈现时间用于 GIF 或动画 PNG 等动画内容)
  • <svg> 元素内的 <image> 元素
  • <video> 元素(系统会使用视频的海报图片加载时间或第一帧显示时间,以较早者为准)
  • 一个元素,带有使用 url() 函数(而不是 CSS 渐变)加载的背景图片
  • 包含文本节点或其他内嵌级文本元素子元素的块级元素。

请注意,将元素限制在这一有限集合内是我们有意为之,目的是在开始时尽量简单。随着开展更多研究,未来可能会添加其他元素(例如完全的 <svg> 支持)。

除了仅考虑部分元素之外,LCP 衡量功能还会使用启发法来排除用户可能会认为“无内容”的特定元素。对于基于 Chromium 的浏览器,其中包括:

  • 不透明度为 0 的元素,用户看不到这些元素
  • 覆盖整个视口的元素可能被视为背景而非内容
  • 占位符图片或其他低熵图片,可能无法反映网页的实际内容

浏览器可能会继续改进这些启发词语,以确保达到用户对最大的内容元素是什么的预期。**

这些“内容性”启发式算法可能与 First Contentful Paint (FCP) 使用的启发法不同,后者可能会考虑其中部分元素(例如占位符图片或完整视口图片),即使它们不符合 LCP 候选条件也是如此。尽管两者的名称中都使用了“contentful”,但这两个指标的目的却不同。FCP 衡量的是任何内容绘制到屏幕上的时间,LCP 用于绘制主要内容,以便 LCP 更有选择性。

如何确定元素的大小?

针对 LCP 报告的元素尺寸通常是用户在视口内可见的尺寸。如果该元素延伸至视口之外,或者有任何元素被剪裁或存在不可见“溢出”,这些部分就不会计入元素的尺寸。overflow

对于根据固有尺寸调整过大小的图片元素,报告的尺寸为可见尺寸或固有尺寸(以较小者为准)。

对于文本元素,LCP 只会考虑能够包含所有文本节点的最小矩形。

对于所有元素,LCP 都不会考虑使用 CSS 应用的外边距、内边距或边框。

注意:确定哪些文本节点属于哪些元素有时可能会比较棘手,尤其是对于其子级包含内嵌元素和纯文本节点,但还包含块级元素的元素。关键在于,每个文本节点都属于(且仅属于)其最近的块级祖先元素。在规范术语中:每个文本节点都属于生成其所属文本块的元素。

何时报告 LCP?

网页通常会分阶段加载,因此,网页上最大的元素可能会发生变化。

为了应对这种可能发生的变化,浏览器在绘制完第一帧后,会立即分派 largest-contentful-paint 类型的 PerformanceEntry,用于标识最大的内容元素。不过,在渲染后续帧后,只要最大内容元素发生变化,它就会再分派一个 PerformanceEntry

例如,在包含文字和主打图片的网页上,浏览器最初可能只会呈现相应文字,此时浏览器会分派 largest-contentful-paint 条目,其 element 属性可能会引用 <p> 或 <h1>。之后,主打图片完成加载后,系统会分派第二个 largest-contentful-paint 条目,并且其 element 属性会引用 <img>

元素只有在呈现并对用户可见后,才能被视为最大的内容元素。尚未加载的图片不会被视为“已渲染”。在字体屏蔽期间,文本节点也不会使用网页字体。在这种情况下,系统可能会将较小的元素报告为最大的内容元素,但如果这个较大的元素呈现完毕,系统便会创建另一个 PerformanceEntry

除了延迟加载图片和字体之外,网页还可能会在新内容可用时向 DOM 添加新元素。如果这些新元素中的任何一个大于之前的最大内容元素,系统还会报告新的 PerformanceEntry

如果从视口甚至 DOM 中移除了最大的内容元素,除非渲染了较大的元素,否则它仍然是最大的内容元素。

注意:在 Chrome 88 之前,已移除的元素不会被视为最大的内容元素,并且移除当前候选元素会触发系统分派新的 largest-contentful-paint 条目。不过,由于常见的界面模式(例如图片轮播界面)经常会移除 DOM 元素,因此我们更新了该指标,以便更准确地反映用户体验。如需了解详情,请参阅 CHANGELOG

一旦用户与网页互动(通过点按、滚动或按键),浏览器就会停止报告新的条目,因为用户互动通常会改变向用户显示的内容(特别是滚动时)。

为便于分析,您应该仅向分析服务报告最近分派的 PerformanceEntry

注意:由于用户可以在后台标签页中打开页面,因此有可能在用户聚焦到标签页之前不会分派 largest-contentful-paint 条目,而该标签页可能比他们首次加载标签页的时间晚得多。如果网页是在后台加载的,用于衡量 LCP 的 Google 工具不会报告此指标,因为它不能反映用户感知的加载时间。

加载时间与呈现时间

出于安全考虑,对于缺少 Timing-Allow-Origin 标头的跨源图片,系统不会公开图片的渲染时间戳。相反,只会公开其加载时间(因为已通过许多其他 Web API 公开)。

这可能会导致一种看似不可能的情况,即 Web API 报告的 LCP 早于 FCP。事实并非如此,而是由于此安全限制而出现。

如果可能,我们始终建议您设置 Timing-Allow-Origin 标头,以便指标更加准确。

如何处理元素布局和尺寸更改?

为了降低计算和调度新的性能条目的性能开销,对元素的大小或位置所做的更改不会生成新的 LCP 候选版本。系统只会考虑元素的初始尺寸和在视口中的位置。

也就是说,系统可能不会报告最初在屏幕外呈现后又在屏幕上过渡的图片。这也意味着,最初在视口中渲染但后来被下推,而不会显示在视图之外的元素仍会报告其初始视口内尺寸。

示例

下面列举了一些示例来说明何时会在一些热门网站上发生 Largest Contentful Paint:

来自 cnn.com 的 Largest Contentful Paint 时间轴

来自 cnn.com 的 LCP 时间轴。

来自 techcrunch.com 的 Largest Contentful Paint 时间轴

来自 techcrunch.com 的 LCP 时间轴。

在上面的两个时间轴中,最大的元素会随着内容加载而变化。在第一个示例中,我们向 DOM 添加了新内容,并改变了最大的元素。在第二个示例中,布局会发生变化,之前最大的内容已从视口中移除。

虽然延迟加载的内容通常会大于网页上已有的内容,但其实并非如此。以下两个示例显示的是在网页完全加载之前发生的 LCP。

来自 instagram.com 的 Largest Contentful Paint 时间轴

来自 instagram.com 的 LCP 时间轴。

来自 google.com 的 Largest Contentful Paint 时间轴

来自 google.com 的 LCP 时间轴。

在第一个示例中,Instagram 徽标的加载时间相对较早,即使其他内容逐渐显示,该徽标也仍然是最大的元素。在 Google 搜索结果页示例中,最大的元素是在任何图片或徽标加载完毕之前显示的一段文本。由于所有单独的图片都小于此段落,因此在整个加载过程中,该图片仍然是最大的元素。

注意:在 Instagram 时间轴的第一帧中,您可能会注意到相机徽标周围没有绿色方框。这是因为它是一个 <svg> 元素,而 <svg> 元素目前不被视为 LCP 候选元素。第一个 LCP 候选版本是第二帧中的文本。

如何衡量 LCP

LCP 可以在试验场景下实际使用场景测量,并且可在以下工具中使用:

实用工具

实验工具

在 JavaScript 中衡量 LCP

如需在 JavaScript 中衡量 LCP,您可以使用 Largest Contentful Paint API。以下示例展示了如何创建用于监听 largest-contentful-paint 条目并将其记录到控制台的 PerformanceObserver

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 条目记录到控制台,但在 JavaScript 中测量 LCP 则更为复杂。如需了解详情,请参阅指标和 API 之间的差异

在上面的示例中,记录的每个 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,您应考虑 LCP。子框架可以使用该 API 将其 largest-contentful-paint 条目报告给父框架以进行汇总。
  • API 从导航开始就测量 LCP,但对于预渲染的网页,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() 的源代码

注意:在某些情况下(例如跨源 iframe),无法在 JavaScript 中衡量 LCP。如需了解详情,请参阅 web-vitals 库的限制部分。

如果最大的元素并不重要,该怎么办?

在某些情况下,网页上最重要的元素与最大的元素不同,开发者可能更关注衡量其他这些元素的呈现时间。这可以使用 Element Timing API 实现,如自定义指标一文中所述。

参考