Core Web Vitals 指标 - Largest Contentful Paint (LCP) 最大内容绘制

120 阅读8分钟

good-lcp-values.svg

Largest Contentful Paint (LCP) 是 Core Web Vitals 三大核心指标之一,用于衡量页面加载性能,具体指用户从开始加载页面到视口中最大内容元素(如图像、视频或文本块)完全渲染完成的时间。

LCP 会报告视口内可见的最大图片、文本块或视频的渲染时间(相对于用户首次导航到网页的时间)

包含哪些元素?

根据Largest Contentful Paint API中所述,系统会考虑以下类型的元素来计算 Largest Contentful Paint:

  • <img> 元素(第一帧呈现时间适用于 GIF 或动画 PNG 等动画内容)
  • <svg> 元素中的 <image> 元素
  • <video> 元素(使用海报图片加载时间或视频的第一帧呈现时间,以较早者为准)
  • 使用 url() 函数加载背景图片的元素
  • 包含文本节点的块级元素或其他内联文本元素子元素。

元素的大小是怎么确定的?

LCP 报告的元素大小通常是指用户在视口中看到的大小,包含文本节点的快级元素或文本元素,大小是包含文字大小决定的,不是块级元素的w*h

  • 如果元素超出视口范围,或者元素的任何部分被剪裁或具有不可见的溢出,则这些部分不会计入元素的大小。
  • 对于已从固有大小调整大小的图片元素,系统会报告较小的可见大小或固有大小。
  • 对于文本元素,LCP 仅考虑可包含所有文本节点的最小矩形。
  • 对于所有元素,LCP 均不会考虑使用 CSS 应用的外边距、内边 距或边框。

LCP的每条记录是什么时候产生的?

网页通常分阶段加载,因此网页上最大的元素可能会发生变化。浏览器会在绘制第一帧后立即调度类型为 largest-contentful-paint 的 PerformanceEntry,以标识最大的内容元素。但是,在渲染后续帧后,每当 Largest Contentful Element 发生变化时,它都会调度另一个 PerformanceEntry

几个关键点:

  • 只有在元素呈现并可供用户看到后,才能将其视为包含内容的最大元素。尚未加载的图片不计入“呈现”次数。在字体阻塞周期内,文本节点也不会使用 Web 字体。在这种情况下,系统可能会报告较小的元素为最大内容渲染时间元素,但一旦较大的元素完成渲染,系统就会创建另一个 PerformanceEntry
  • 除了延迟加载的图片和字体之外,向 DOM 添加新元素。如果其中任何新元素的大小大于之前最大的有内容元素,系统也会记录新的 PerformanceEntry
  • 如果从视口中移除(甚至从 DOM 中移除)最大的内容元素,除非渲染出更大的元素,否则它仍然是最大的内容元素。
  • 一旦用户与网页互动(通过点按、滚动或按键操作),浏览器就会停止记录新条目。

元素布局和大小更改了怎么处理? 对元素的大小或位置所做的更改不会生成新的 LCP 候选项。系统仅会考虑元素在视口中的初始尺寸和位置。这意味着:

  1. 最初在屏幕外呈现,然后转换到屏幕上的图片可能不会被记录。
  2. 最初在视口中呈现但随后被推下、超出视野的元素仍会记录其初始的视口内大小。

获取 largest-contentful-paint 记录方式:

// 此代码展示了如何将 `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});

实例 :

<script>
  // 添加 max-box
  setTimeout(() => {
    const div = document.createElement('div');
    div.style.width = '300px';
    div.style.height = '300px';
    div.style.backgroundColor = 'green';
    div.classList.add('max-box');

    const textNode = document.createTextNode('1');
    div.appendChild(textNode);
    document.body.appendChild(div);
  }, 1000)

  // 添加 img 1920 * 900
  setTimeout(() => {
    const img = new Image();
    img.src = 'https://web.developers.google.cn/static/articles/optimize-lcp/image/a-network-waterfall-the-919387402b1d3_856.png?hl=zh-cn'
    document.body.appendChild(img);
  }, 2000)

  // 修改 min-box 的大小
  setTimeout(() => {
    console.log('change size');
    const div = document.getElementById('min-box');
    div.style.width = '360px';
    div.style.height = '360px';
  }, 3000)
</script>

<body>
  <div id="min-box" style="width: 200px; height: 100px; background-color: antiquewhite;">
    <span>1</span>
  </div>
</body>

控制台打印结果:

Pasted_image_20250520155854.png

修改min-box并没有打印新的记录,但是max-box没有打印出来。

实例 2:修改max-box的文本内容为12

控制台打印结果:

Pasted_image_20250520160203.png

max-box 打印了

指标计算

在上面的示例中,每个记录的 largest-contentful-paint 条目都代表当前的 LCP 候选项。一般来说,发出的最后一个条目的 startTime 值就是 LCP 值,但也不总是如此。并非所有 largest-contentful-paint 条目都适用于衡量 LCP。

指标与 API 之间的差异

  • 该 API 会为在后台标签页中加载的网页分派 largest-contentful-paint 条目,但在计算 LCP 时应忽略这些网页。
  • 网页进入后台后,该 API 将继续调度 largest-contentful-paint 条目,但在计算 LCP 时应忽略这些条目(仅当网页在整个时间都处于前台时,才能考虑元素)。
  • 当网页从返回/前进缓存恢复时,API 不会报告 largest-contentful-paint 条目,但在这些情况下,应衡量 LCP,因为用户会将其视为不同的网页访问。
  • API 不会考虑 iframe 中的元素,但该指标会考虑,因为它们是网页用户体验的一部分。
  • 该 API 会从导航开始时刻开始衡量 LCP,但对于预渲染网页,应从 activationStart 开始衡量 LCP,因为这与用户体验到的 LCP 时间相对应。

开发者无需记住所有这些细微差异,只需使用 [web-vitals ] (github.com/GoogleChrom… LCP,该库会处理这些差异(在可能的情况下,请注意不涵盖 iframe 问题):

import {onLCP} from 'web-vitals';

onLCP(console.log);

优化 LCP

LCP 的标准

使用 Chrome 开发者工具 可以看到 LCP 的指标信息,以及细分的四个子部分。

LCP描述
<= 2.5s良好
2.5s - 4s需要改进
>4s较差

Pasted_image_20250521100750.png

Pasted_image_20250521100646.png

最佳子部分时间

LCP 子部分LCP 占比 (%)
加载第一个字节所需时间~40%
资源加载延迟<10%
资源加载时长~40%
元素渲染延迟<10%
总计100%

各部分优化

减少资源加载延迟

一般来说,有两个因素会影响 LCP 资源的加载速度:

  • 发现资源的时间。
  • 资源的优先级。
  1. 在发现资源时进行优化
  • LCP 元素是 <img> 元素,其 src 或 srcset 属性存在于初始 HTML 标记中。
  • LCP 元素需要 CSS 背景图片,但该图片是在 HTML 标记中使用 <link rel="preload">(或使用 Link 标头)预加载的。
  • LCP 元素是一个文本节点,需要 Web 字体才能呈现,并且该字体是在 HTML 标记中使用 <link rel="preload">(或使用 Link 标头)加载的。
<!-- 加载将引用LCP图片的样式表 -->
<link rel="stylesheet" href="/path/to/styles.css">

<!-- 预加载LCP图片并设置高fetchpriority,以便其与样式表同时开始加载。 -->
<link rel="preload" fetchpriority="high" as="image" href="/path/to/hero-image.webp" type="image/webp">
  1. 优化资源的优先级
  • 如果 LCP 元素是图片可以设置 fetchpriority="high"
<img fetchpriority="high" src="/path/to/hero-image.webp">
减少元素渲染延迟

此步骤的目标是确保 LCP 元素可在其资源完成加载(无论何时)后立即渲染

LCP 元素在其资源完成加载后_无法_立即渲染的主要原因是,渲染因其他原因而被阻止

  • 由于 <head> 中的样式表或同步脚本仍在加载,因此整个页面的呈现被阻止。
  • LCP 资源已加载完毕,但 LCP 元素尚未添加到 DOM(正在等待某些 JavaScript 代码加载)。
  • 该元素被其他代码隐藏了,例如仍在确定用户应参与哪项实验的 A/B 测试库。
  • 主线程因长时间运行的任务而被阻塞,渲染工作需要等到这些长时间运行的任务完成。

对应优化:

  • 减少阻塞渲染的样式表或将其内嵌,HTML 加载样式表会阻止渲染其后面的所有内容。
  • 延迟或内嵌会阻止渲染的 JavaScript。
  • 使用服务器端渲染(SSR),SSR 有两个主要优势:图片资源将可从 HTML 源代码中找到;网页内容无需等待其他 JavaScript 请求完成。主要缺点是需要额外的服务器处理时间,这可能会减慢 TTFB。不过,这种权衡通常是值得的,因为服务器处理时间在您的控制范围内,而用户的网络和设备功能则不在您的控制范围内。
  • 拆分长任务
缩短资源加载时长

此步骤的目标是减少将资源字节通过网络传输到用户设备所花费的时间。通常,有以下四种方法可以实现此目的:

  1. 缩减资源的大小。
  1. 减少资源传输的距离。
  2. 减少网络带宽争用。
  3. 完全消除网络时间。
缩短加载第一个字节所需时间

有关优化 TTFB 的具体指导,请参阅优化 TTFB 指南

资料:

扩展: [[判断元素是否在视口内]]