优化最大内容ful Paint

关于如何分解 LCP 并确定关键改进领域的循序渐进指南。

发布时间:2020 年 4 月 30 日

最大内容ful Paint (LCP) 是三个 Core Web Vitals 指标之一,它表示网页主要内容的加载速度。具体来说,LCP 衡量的是从用户发起页面加载到视口中呈现最大图像或文本块的时间。

为了提供良好的用户体验,网站应力求至少 75% 的页面访问 LCP 低于或等于 2.5 秒。

Good LCP values are 2.5 seconds or less, poor values are greater than 4.0 seconds, and anything in between needs improvement
良好的 LCP 值是 2.5 秒或更短。

许多因素会影响浏览器加载和呈现网页的速度,其中任何一个环节的延迟都可能对 LCP 产生重大影响。

很少有针对页面某个部分的快速修复能够显着改善 LCP。要改进 LCP,您必须查看整个加载过程,并确保沿途的每个步骤都经过优化。

了解您的 LCP 指标

在优化 LCP 之前,开发者应先了解他们是否确实存在 LCP 问题,以及任何此类问题的程度。

LCP 可以通过多种工具进行衡量,并非所有工具都以相同的方式衡量 LCP。要了解真实用户的 LCP,我们应该关注真实用户的体验,而不是 Lighthouse 或本地测试等基于实验室的工具所显示的结果。这些基于实验室的工具可以提供大量信息来解释和帮助您改进 LCP,但请注意,仅靠实验室测试可能无法完全代表您的实际用户体验。

基于真实用户的 LCP 数据可以从安装在网站上的真实用户监控 (RUM) 工具中浮出水面,或者使用 Chrome 用户体验报告 (CrUX),该报告从数百万个网站的真实 Chrome 用户那里收集匿名数据。

使用 Chrome DevTools CrUX LCP 数据

Chrome DevTools 的 Performance 面板在 实时指标视图中显示您的本地 LCP 体验以及页面或来源的 CrUX LCP。

Local and field LCP in the Chrome DevTools Performance panel
Chrome DevTools Performance 面板中的本地和现场 LCP。

通过将现场数据分层到 Performance 面板上,您可以评估页面是否存在任何真实用户 LCP 问题,并调整您的本地环境设置以更好地重现和调试这些问题。

使用 PageSpeed Insights CrUX LCP 数据

PageSpeed Insights 在标记为发现您的真实用户体验的顶部部分提供对 CrUX 数据的访问。更详细的基于实验室的数据在标记为诊断性能问题的底部部分提供。如果您的网站有 CrUX 数据可用,请始终首先关注真实用户数据。

CrUX data shown in PageSpeed Insights
PageSpeed Insights 中显示的 CrUX 数据。

PageSpeed Insights 最多显示四个不同的 CrUX 数据

  • 移动设备 针对 此网址 的数据
  • 桌面设备 针对 此网址 的数据
  • 移动设备 针对整个 来源 的数据
  • 桌面设备 针对整个 来源 的数据

您可以在本节顶部和右上角的控件中切换这些数据。如果网址没有足够的数据在网址级别显示,但来源有数据,PageSpeed Insights 始终显示来源数据。

PageSpeed Insight falling back to origin-level data where url-level data is not available
当 PageSpeed Insights 没有网址级别数据时,它会显示来源级别数据。

整个来源的 LCP 可能与单个页面的 LCP 非常不同,具体取决于该页面上的 LCP 加载方式与其他来源页面相比如何。它还可能受到访问者如何导航到这些页面的影响。主页往往由新用户访问,因此通常以“冷”加载方式加载,没有任何缓存内容,因此通常是网站上最慢的页面。

查看 CrUX 数据的四个不同类别可以帮助您了解 LCP 问题是特定于此页面,还是更普遍的站点范围问题。同样,它可以显示哪些设备类型存在 LCP 问题。

使用 PageSpeed Insights CrUX 补充指标

希望优化 LCP 的用户还应使用 首次内容ful Paint (FCP)Time to First Byte (TTFB) 计时,这些都是良好的诊断指标,可以为 LCP 提供有价值的见解。

TTFB 是指访问者开始导航到页面(例如,点击链接)到接收到 HTML 文档的第一个字节之间的时间。高 TTFB 可能会使实现 2.5 秒 LCP 变得具有挑战性,甚至不可能。

高 TTFB 可能是由于多个服务器重定向、访问者位于远离最近站点服务器的位置、访问者网络条件差或由于查询参数而无法使用缓存内容。

一旦页面开始呈现,可能会出现初始绘制(例如,背景颜色),然后是一些内容的出现(例如,站点标头)。初始内容的出现由 FCP 衡量。TTFB 和其他指标之间的差异可能非常说明问题。

TTFB 和 FCP 之间的较大差异可能表明浏览器需要下载大量渲染阻塞型资源。这也可能表明它必须完成大量工作才能呈现任何有意义的内容,这是严重依赖客户端渲染的站点的典型迹象。

FCP 和 LCP 之间的较大差异表明 LCP 资源要么无法立即供浏览器优先处理(例如,由 JavaScript 管理而不是在初始 HTML 中可用的文本或图像),要么浏览器在显示 LCP 内容之前正在完成其他工作。

使用 PageSpeed Insights Lighthouse 数据

PageSpeed Insights 的 Lighthouse 部分提供了一些改进 LCP 的指南,但首先您应该检查给出的 LCP 是否与 CrUX 提供的真实用户数据大致一致。如果 Lighthouse 和 CrUX 不同意,那么 CrUX 可能更准确地反映了您的用户体验。在您采取行动之前,请确保您的 CrUX 数据是针对您的页面,而不是整个来源。

如果 Lighthouse 和 CrUX 都显示需要改进的 LCP 值,则 Lighthouse 部分可以提供有关如何改进 LCP 的宝贵指导。使用 LCP 过滤器仅显示与 LCP 相关的审核,如下所示

Lighthouse LCP Opportunities and Diagnostics
Lighthouse 诊断和改进 LCP 的建议。

除了机会改进之外,还有诊断信息可以提供更多信息来帮助诊断问题。最大内容ful Paint 元素诊断显示了构成 LCP 的各种计时的有用分解

LCP subparts in Lighthouse
Lighthouse 的 LCP 元素分解。

LCP 资源类型和子部分也在 CrUX 中可用

接下来我们将深入探讨这些子部分。

LCP 分解

当 PageSpeed Insights 没有给出关于如何改进此指标的答案时,优化 LCP 可能是一项更复杂的任务。对于复杂的任务,通常最好将其分解为更小、更易于管理的任务,并分别解决每个任务。

本节介绍了一种将 LCP 分解为其最关键的子部分的方法,然后介绍有关如何优化每个部分的具体建议和最佳实践。

大多数页面加载通常包括许多网络请求,但为了识别改进 LCP 的机会,您应该首先查看以下两个请求

  1. 初始 HTML 文档
  2. LCP 资源(如果适用)

虽然页面上的其他请求可能会影响 LCP,但这两个请求(特别是 LCP 资源开始和结束的时间)揭示了您的页面是否针对 LCP 进行了优化。

要识别 LCP 资源,您可以使用开发者工具(例如之前讨论的 PageSpeed Insights、Chrome DevToolsWebPageTest)来确定 LCP 元素。从那里,您可以将 URL(再次,如果适用)与页面加载的所有资源的 网络瀑布流相匹配。

例如,以下可视化效果在典型页面加载的网络瀑布流图中突出显示了这些资源,其中 LCP 元素需要图像请求才能呈现。

A network waterfall with the HTML and LCP resources highlighted
瀑布流图,显示网页 HTML 和 LCP 需要的资源的加载时间。

对于经过良好优化的页面,您希望 LCP 资源请求尽早开始加载,并且您希望 LCP 元素在 LCP 资源完成加载后尽快呈现。为了帮助可视化特定页面是否遵循此原则,您可以将总 LCP 时间分解为以下子部分

Time to First Byte (TTFB)
从用户发起页面加载到浏览器接收到 HTML 文档响应的第一个字节之间的时间。
资源加载延迟
TTFB 和浏览器开始加载 LCP 资源之间的时间。如果 LCP 元素不需要资源加载即可呈现(例如,如果该元素是用系统字体呈现的文本节点),则此时间为 0。
资源加载持续时间
加载 LCP 资源本身所需的时间。如果 LCP 元素不需要资源加载即可呈现,则此时间为 0。
元素渲染延迟
LCP 资源完成加载到 LCP 元素完全呈现之间的时间。

每个页面的 LCP 都由这四个子类别组成。它们之间没有间隙或重叠,并且它们加起来就是完整的 LCP 时间。

A breakdown of LCP showing the four subcategories
相同的瀑布流图,时间轴上叠加了四个 LCP 子类别。

每个页面都可以将其 LCP 值分解为这四个子部分。它们之间没有重叠或间隙。总而言之,它们加起来就是完整的 LCP 时间。

优化 LCP 时,尝试单独优化这些子部分会很有帮助。但同样重要的是要记住,您需要优化所有这些子部分。在某些情况下,应用于某个部分的优化不会改善 LCP,它只会将节省的时间转移到另一个部分。

例如,在之前的网络瀑布流中,如果您通过更多地压缩图像或切换到更优格式(例如 AVIF 或 WebP)来减小图像的文件大小,那将缩短资源加载持续时间,但实际上不会改善 LCP,因为时间只会转移到元素渲染延迟子部分

The same breakdown of LCP shown earlier where the resource load duration subcategory is shortened, but the overall LCP time remains the same.
缩短资源加载持续时间会增加元素渲染延迟,而不会减少 LCP。

发生这种情况的原因是,在此页面上,LCP 元素在 JavaScript 代码完成加载之前处于隐藏状态,然后所有内容会一次性显示出来。

此示例有助于说明您需要优化所有这些子部分才能实现最佳 LCP 结果的观点。

最佳子部分时间

为了优化 LCP 的每个子部分,了解这些子部分在经过良好优化的页面上的理想分解情况非常重要。

在四个子部分中,有两个在名称中带有“延迟”一词。这是一个线索,表明您希望将这些时间尽可能接近于零。另外两个部分涉及网络请求,就其本质而言,这需要时间。

LCP 子部分 LCP 百分比
Time to first byte ~40%
资源加载延迟 <10%
资源加载持续时间 ~40%
元素渲染延迟 <10%
总计 100%

请注意,这些时间分解是指导方针,而不是严格的规则。如果您的页面上的 LCP 时间始终在 2.5 秒内,那么相对比例是多少就无关紧要了。但是,如果您在任何一个“延迟”部分花费了大量不必要的时间,那么将很难始终达到 2.5 秒的目标

考虑 LCP 时间分解的一个好方法是

  • 绝大多数 LCP 时间应花费在加载 HTML 文档和 LCP 资源上。
  • 在 LCP 之前的任何时间,如果这两个资源之一没有加载,那么这就是改进的机会

如何优化每个部分

现在您已经了解了在经过良好优化的页面上,每个 LCP 子部分时间应该如何分解,您就可以开始优化您自己的页面了。

接下来的四个部分将介绍有关如何优化每个部分的建议和最佳实践。它们按顺序呈现,从可能产生最大影响的优化开始。

1. 消除资源加载延迟

此步骤的目标是确保 LCP 资源尽早开始加载。虽然理论上资源可能最早开始加载的时间是 TTFB 之后立即,但在实践中,浏览器在实际开始加载资源之前总是会存在一些延迟。

一个好的经验法则是,您的 LCP 资源应与该页面加载的第一个资源同时开始加载。或者,换句话说,如果 LCP 资源开始加载的时间晚于第一个资源,那么就存在改进的机会。

A network waterfall diagram showing the LCP resource starting after the first resource, showing the opportunity for improvement
在此页面上,LCP 资源开始加载的时间远晚于第一个加载的样式表。这里有改进的空间。

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

  • 资源何时被发现。
  • 资源被赋予的优先级。

优化资源被发现的时间

为了确保您的 LCP 资源尽早开始加载,至关重要的是,浏览器 预加载扫描器可以在初始 HTML 文档响应中发现该资源。例如,在以下情况下,浏览器可以通过扫描 HTML 文档响应来发现 LCP 资源

  • LCP 元素是 <img> 元素,并且其 srcsrcset 属性存在于初始 HTML 标记中。
  • LCP 元素需要 CSS 背景图像,但该图像已使用 HTML 标记中的 <link rel="preload">(或使用 Link 标头)进行预加载。
  • LCP 元素是一个文本节点,需要 Web 字体才能呈现,并且该字体已使用 HTML 标记中的 <link rel="preload">(或使用 Link 标头)加载。

以下是一些 LCP 资源无法从扫描 HTML 文档响应中发现的示例

  • LCP 元素是使用 JavaScript 动态添加到页面的 <img>
  • LCP 元素使用 JavaScript 库延迟加载,该库隐藏了其 srcsrcset 属性(通常为 data-srcdata-srcset)。
  • LCP 元素需要 CSS 背景图像。

在每种情况下,浏览器都需要运行脚本或应用样式表(通常涉及等待网络请求完成),然后才能发现 LCP 资源并开始加载它。这绝不是最佳做法。

为了消除不必要的资源加载延迟,您的 LCP 资源应可从 HTML 源代码中发现。在资源仅从外部 CSS 或 JavaScript 文件引用的情况下,应使用高 获取优先级预加载 LCP 资源,例如

<!-- Load the stylesheet that will reference the LCP image. -->
<link rel="stylesheet" href="/path/to/styles.css">

<!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. -->
<link rel="preload" fetchpriority="high" as="image" href="/path/to/hero-image.webp" type="image/webp">

优化资源被赋予的优先级

即使 LCP 资源可以从 HTML 标记中发现,它仍然可能无法与第一个资源一样早地开始加载。如果浏览器预加载扫描器的优先级启发式方法无法识别该资源很重要,或者如果它确定其他资源更重要,则可能会发生这种情况。

例如,如果您在 <img> 元素上设置 loading="lazy",则可以使用 HTML 延迟您的 LCP 图像。使用延迟加载意味着该资源要等到布局确认图像在视口中后才会加载,因此可能会比原本更晚开始加载。

即使没有延迟加载,图像最初也不会被浏览器以最高优先级加载,因为它们不是渲染阻塞型资源。您可以使用 fetchpriority 属性来提示浏览器哪些资源最重要,以用于可能从更高优先级中受益的资源

<img fetchpriority="high" src="/path/to/hero-image.webp">

如果您认为 <img> 元素很可能是您页面的 LCP 元素,则最好在其上设置 fetchpriority="high"。但是,为多个或两个以上的图像设置高优先级会使优先级设置在减少 LCP 方面无济于事。

您还可以降低文档响应中可能较早但由于样式设置而不可见的图像的优先级,例如启动时不可见的轮播幻灯片中的图像

<img fetchpriority="low" src="/path/to/carousel-slide-3.webp">

降低某些资源的优先级可以为更需要的资源提供更多带宽,但请小心。始终检查 DevTools 中的资源优先级,并使用实验室和现场工具测试更改。

在您优化了 LCP 资源优先级和发现时间后,您的网络瀑布流应如下所示(LCP 资源与第一个资源同时开始)

A network waterfall diagram showing the LCP resource now starting at the same time as the first resource
LCP 资源现在与样式表同时开始加载。

2. 消除元素渲染延迟

此步骤的目标是确保 LCP 元素在其资源完成加载后立即可以呈现,无论何时发生这种情况。

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

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

以下部分解释了如何解决不必要的元素渲染延迟的最常见原因。

减少或内联渲染阻塞型样式表

从 HTML 标记加载的样式表会阻止所有后续内容的渲染,这很好,因为您通常不希望渲染无样式的 HTML。但是,如果样式表太大,以至于加载时间明显长于 LCP 资源,那么它将阻止 LCP 元素呈现,即使在其资源完成加载后也是如此,如本例所示

A network waterfall diagram showing a large CSS file blocking rendering of the LCP element because it takes longer to load than the LCP resource
图像和样式表同时开始加载,但图像要等到样式表准备就绪后才能呈现。

要解决此问题,您的选择是

  • 将样式表内联到 HTML 中,以避免额外的网络请求;或者,
  • 减小样式表的大小。

总的来说,只有当你的样式表很小时才建议内联样式表,因为 HTML 中的内联内容无法从后续页面加载中的缓存中获益。如果样式表太大,以至于加载时间超过 LCP 资源,那么它不太可能成为内联的良好候选对象。

在大多数情况下,确保样式表不阻止 LCP 元素渲染的最佳方法是减小其大小,使其小于 LCP 资源。这应确保它不会成为大多数访问的瓶颈。

以下是一些减小样式表大小的建议:

延迟或内联渲染阻塞 JavaScript

几乎从不需要向页面的 <head> 添加同步脚本(没有 asyncdefer 属性的脚本),这样做几乎总是会对性能产生负面影响。

在需要尽早运行 JavaScript 代码的情况下,最好将其内联,这样渲染就不会因为等待另一个网络请求而被延迟。但是,与样式表一样,只有当脚本非常小时才应内联脚本。

不要这样做
<head>
  <script src="/path/to/main.js"></script>
</head>
这样做
<head>
  <script>
    // Inline script contents directly in the HTML.
    // IMPORTANT: only do this for very small scripts.
  </script>
</head>

使用服务器端渲染

服务器端渲染 (SSR) 是在服务器上运行客户端应用程序逻辑,并使用完整的 HTML 标记响应 HTML 文档请求的过程。

从优化 LCP 的角度来看,SSR 有两个主要优势:

  • 你的图片资源将可以从 HTML 源代码中被发现(如步骤 1 前面讨论的那样)。
  • 你的页面内容不需要额外的 JavaScript 请求才能完成渲染。

SSR 的主要缺点是它需要额外的服务器处理时间,这可能会减慢你的 TTFB。但是,这种权衡通常是值得的,因为服务器处理时间在你的控制范围内,而用户的网络和设备能力则不在。

与 SSR 类似的选项称为静态站点生成 (SSG) 或预渲染。这是在构建步骤而不是按需生成 HTML 页面的过程。如果你的架构允许预渲染,那么它通常是性能方面更好的选择。

分解长任务

即使你遵循了之前的建议,并且你的 JavaScript 代码既不阻塞渲染,也不负责渲染你的元素,它仍然可能延迟 LCP。

发生这种情况的最常见原因是页面加载大型 JavaScript 文件,这些文件需要在浏览器的主线程上解析和执行。这意味着,即使你的图片资源已完全下载,它可能仍然需要等到不相关的脚本完成执行后才能渲染。

如今,所有浏览器都在主线程上渲染图像,这意味着任何阻塞主线程的操作都可能导致不必要的元素渲染延迟

3. 减少资源加载时长

此步骤的目标是减少通过网络将资源字节传输到用户设备所花费的时间。一般来说,有四种方法可以做到这一点:

  • 减小资源的大小。
  • 缩短资源必须传播的距离。
  • 减少网络带宽的争用。
  • 完全消除网络时间。

减小资源的大小

页面的 LCP 资源(如果存在)将是图像或 Web 字体。以下指南详细介绍了如何减小两者的大小:

缩短资源必须传播的距离

除了减小资源的大小之外,你还可以通过使你的服务器在地理位置上尽可能靠近用户来缩短加载时间。而做到这一点的最佳方法是使用内容分发网络 (CDN)。

图像 CDN 特别有用,因为它们不仅缩短了资源必须传播的距离,而且通常还会减小资源的大小——为你自动实施之前的所有尺寸减小建议。

减少网络带宽的争用

即使你减小了资源的大小以及它必须传播的距离,如果同时加载许多其他资源,资源仍然可能需要很长时间才能加载。这个问题被称为网络争用

如果你已为你的 LCP 资源赋予fetchpriority尽快开始加载它,那么浏览器将尽最大努力阻止较低优先级的资源与其竞争。但是,如果你正在加载许多具有高 fetchpriority 的资源,或者只是在加载大量资源,那么这可能会影响 LCP 资源的加载速度。

完全消除网络时间

减少资源加载时长的最佳方法是从过程中完全消除网络。如果你使用高效的缓存控制策略提供你的资源,那么第二次请求这些资源的访问者将从缓存中获取它们——将资源加载时长基本上降至零!

如果你的 LCP 资源是 Web 字体,除了减小 Web 字体大小之外,你还应该考虑是否需要阻止 Web 字体资源加载时的渲染。如果你将 font-display 值设置为除 autoblock 之外的任何值,那么文本将在加载期间始终可见,并且 LCP 不会被额外的网络请求阻止。

最后,如果你的 LCP 资源很小,那么将资源内联为数据 URL可能是有意义的,这也将消除额外的网络请求。但是,使用数据 URL 存在一些注意事项,因为这样资源无法被缓存,并且在某些情况下可能会由于额外的解码成本而导致更长的渲染延迟。

4. 减少首字节时间

此步骤的目标是尽快交付初始 HTML。此步骤被列为最后一步,因为它通常是开发者最无法控制的一步。但是,它也是最重要的步骤之一,因为它直接影响后续的每个步骤。在后端交付内容的第一个字节之前,前端不会发生任何事情,因此任何可以加快 TTFB 的操作都将改善所有其他加载指标。

对于原本快速的网站来说,TTFB 缓慢的常见原因是访问者通过多次重定向到达,例如来自广告或短链接。始终最大限度地减少访问者必须等待的重定向次数。

另一个常见原因是当无法从 CDN 边缘服务器使用缓存内容,并且所有请求都必须定向回源服务器时。如果访问者使用唯一的 URL 参数进行分析(即使它们不会导致不同的页面),也可能发生这种情况。

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

在 JavaScript 中监控 LCP 分解

前面讨论的所有 LCP 子部分的计时信息都可以在 JavaScript 中通过以下性能 API 的组合来获得:

在 JavaScript 中计算这些计时值的优势在于,它允许你将它们发送给分析提供商或记录到你的开发者工具中,以帮助进行调试和优化。

例如,以下屏幕截图使用了用户定时 API 中的 performance.measure() 方法,以向 Chrome DevTools Performance 面板中的 Timings 轨道添加条形图。

User Timing measures of the LCP subcategories visualized in Chrome DevTools
Timings 轨道显示 LCP 子类别的时间轴。

当与 NetworkMain thread 轨道一起查看时,Timings 轨道中的可视化效果特别有用,因为你可以一目了然地看到在这些时间跨度内页面上还发生了什么。

除了在 timings 轨道中可视化 LCP 子部分之外,你还可以使用 JavaScript 来计算每个子部分占总 LCP 时间的百分比。有了这些信息,你可以确定你的页面是否满足前面描述的建议的百分比分解

此屏幕截图显示了一个示例,该示例将每个 LCP 子部分的总时间及其占总 LCP 时间的百分比记录到控制台。

The LCP subcategory times, as well as their percent of LCP, printed to the console
LCP 子类别计时和百分比。

这两个可视化效果都是使用以下代码创建的:

const LCP_SUBPARTS = [
  'Time to first byte',
  'Resource load delay',
  'Resource load duration',
  'Element render delay',
];

new PerformanceObserver((list) => {
  const lcpEntry = list.getEntries().at(-1);
  const navEntry = performance.getEntriesByType('navigation')[0];
  const lcpResEntry = performance
    .getEntriesByType('resource')
    .filter((e) => e.name === lcpEntry.url)[0];

  // Ignore LCP entries that aren't images to reduce DevTools noise.
  // Comment this line out if you want to include text entries.
  if (!lcpEntry.url) return;

  // Compute the start and end times of each LCP subpart.
  // WARNING! If your LCP resource is loaded cross-origin, make sure to add
  // the `Timing-Allow-Origin` (TAO) header to get the most accurate results.
  const ttfb = navEntry.responseStart;
  const lcpRequestStart = Math.max(
    ttfb,
    // Prefer `requestStart` (if TOA is set), otherwise use `startTime`.
    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0
  );
  const lcpResponseEnd = Math.max(
    lcpRequestStart,
    lcpResEntry ? lcpResEntry.responseEnd : 0
  );
  const lcpRenderTime = Math.max(
    lcpResponseEnd,
    // Use LCP startTime (the final LCP time) because there are sometimes
    // slight differences between loadTime/renderTime and startTime
    // due to rounding precision.
    lcpEntry ? lcpEntry.startTime : 0
  );

  // Clear previous measures before making new ones.
  // Note: due to a bug, this doesn't work in Chrome DevTools.
  LCP_SUBPARTS.forEach((part) => performance.clearMeasures(part));

  // Create measures for each LCP subpart for easier
  // visualization in the Chrome DevTools Performance panel.
  const lcpSubpartMeasures = [
    performance.measure(LCP_SUBPARTS[0], {
      start: 0,
      end: ttfb,
    }),
    performance.measure(LCP_SUBPARTS[1], {
      start: ttfb,
      end: lcpRequestStart,
    }),
    performance.measure(LCP_SUBPARTS[2], {
      start: lcpRequestStart,
      end: lcpResponseEnd,
    }),
    performance.measure(LCP_SUBPARTS[3], {
      start: lcpResponseEnd,
      end: lcpRenderTime,
    }),
  ];

  // Log helpful debug information to the console.
  console.log('LCP value: ', lcpRenderTime);
  console.log('LCP element: ', lcpEntry.element, lcpEntry.url);
  console.table(
    lcpSubpartMeasures.map((measure) => ({
      'LCP subpart': measure.name,
      'Time (ms)': measure.duration,
      '% of LCP': `${
        Math.round((1000 * measure.duration) / lcpRenderTime) / 10
      }%`,
    }))
  );
}).observe({type: 'largest-contentful-paint', buffered: true});

你可以按原样使用此代码进行本地调试,或者修改它以将此数据发送给分析提供商,以便你可以更好地了解你的页面上真实用户的 LCP 分解情况。

总结

LCP 很复杂,其计时可能会受到多种因素的影响。但是,如果你考虑到优化 LCP 主要在于优化 LCP 资源的加载,那么它可以大大简化事情。

在较高层面,优化 LCP 可以概括为四个步骤:

  1. 确保 LCP 资源尽早开始加载。
  2. 确保 LCP 元素在其资源完成加载后即可立即渲染。
  3. 尽可能减少 LCP 资源的加载时间,而不会牺牲质量。
  4. 尽可能快地交付初始 HTML 文档。

如果你能够在你的页面上遵循这些步骤,那么你应该有信心你正在为你的用户提供最佳的加载体验,并且你应该在你的真实 LCP 分数中看到这一点。