LCP 分解与关键优化领域识别:分步指南

作者:林语者 分类:工程代码

LCP 分解与关键优化领域识别:分步指南

发布日期:2020年4月30日
最后更新:2025年3月31日

Largest Contentful Paint(LCP)是 Core Web Vitals 三大指标之一,用于衡量网页主要内容加载的速度。具体而言,LCP 测量的是从用户开始加载页面,到视口内最大图片或文本块完成渲染的时间。

要提供出色的用户体验,网站应确保至少75%的页面访问中LCP低于2.5秒。

  • 良好:LCP ≤ 2.5秒

影响浏览器加载和渲染网页速度的因素众多。其中任何一个环节出现延迟,都可能对 LCP 产生显著影响。

仅对页面进行局部临时修复,很少能带来 LCP 的显著提升。要改善 LCP,需要全面了解加载过程,并对其中所有步骤进行优化。

了解 LCP 指标

在优化 LCP 之前,开发者需要确定是否存在 LCP 问题及其严重程度。

虽然多种工具可以测量 LCP,但并非所有工具都使用相同的方法。要了解真实用户的 LCP 情况,需要关注实际用户体验,而非 Lighthouse 等实验室工具或本地测试得出的数值。这些实验室工具虽然提供了有助于解释和改善 LCP 的丰富信息,但需注意,仅凭实验室测试无法完全代表真实的用户体验。

基于真实用户的 LCP 数据可以从安装在网站上的真实用户监控(RUM)工具获取,或使用 Chrome 用户体验报告(CrUX)。CrUX 从数百万网站的 Chrome 用户中匿名收集数据。

在 Chrome DevTools 中获取 CrUX LCP 数据

Chrome DevTools 的 Performance 面板会在实时指标视图中显示页面或来源的 CrUX LCP,旁边还会显示本地 LCP 体验。此外,在性能追踪的 Insights 部分(稍后详述)也会显示 LCP 子部分的时间细分。

Chrome DevTools Performance 面板中显示的本地 LCP 与字段 LCP。

通过将字段数据叠加到 Performance 面板,可以评估页面是否存在真实用户的 LCP 问题,并通过调整本地环境设置来更好地复现和调试这些问题。

使用 PageSpeed Insights 的 CrUX LCP 数据

在 PageSpeed Insights 中,可以在顶部标记为 See what real users experience 的部分访问 CrUX 数据。标记为 Diagnose performance issues 的底部部分则提供了基于实验室的更详细数据。如果网站提供 CrUX 数据,应始终优先参考真实用户数据。

PageSpeed Insights 中显示的 CrUX 数据。

注意:如果 CrUX 不提供数据(例如,页面流量不足以获取页面级数据),则需要使用在网页上运行的 JavaScript API 收集的 RUM 数据来补充 CrUX。此外,它还能提供比公开数据集更丰富的数据。本指南后续将介绍如何使用 JavaScript 收集这些数据。

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

  • 此 URL 的移动数据
  • 此 URL 的桌面数据
  • 整个来源的移动数据
  • 整个来源的桌面数据

可以通过本部分顶部和右上角的控件切换这些设置。即使某个 URL 没有足够的数据在 URL 级别显示,只要来源有数据,PageSpeed Insights 就会始终显示来源级别的数据。

当 PageSpeed Insights 没有 URL 级别的数据时,会显示来源级别的数据。

整个来源的 LCP 可能与单个页面的 LCP 存在较大差异,具体取决于该页面的加载方式与来源中其他页面的比较结果。同时,用户访问这些页面的方式也会产生影响。例如,主页通常由新用户访问,往往以”冷”状态(缓存中无内容)加载,因此可能成为网站中最慢的页面。

通过比较这四类 CrUX 数据,可以判断 LCP 问题是特定于此页面,还是影响整个网站的普遍现象。同样,还可以查看出现 LCP 问题的设备类型。

使用 PageSpeed Insights 的 CrUX 补充指标

在优化 LCP 时,还应关注首次内容绘制(FCP)和首字节时间(TTFB),它们是能够提供宝贵洞察的优秀诊断指标。

TTFB 衡量的是从站点访问者开始跳转到页面(例如点击链接),到接收到 HTML 文档第一个字节的时间。TTFB 过长可能导致 LCP 难以甚至无法达到 2.5 秒的目标。

TTFB 过长的原因可能包括:多次服务器重定向、用户地理位置远离最近的服务器、网络状况不佳,或者查询参数导致无法使用缓存内容等。

页面开始渲染后,会先进行首次绘制(例如背景色),随后可能显示内容(例如网站页眉)。首次内容的显示由 FCP 衡量。FCP 与其他指标之间的差值具有重要的参考价值。

如果 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 相关的审计项。

Lighthouse 提供的用于改善 LCP 的诊断和建议。

除了改进机会,诊断信息还提供了有助于问题诊断的详细信息。Largest Contentful Paint element 诊断清晰地展示了构成 LCP 的各个计时部分的细分。

Lighthouse 提供的 LCP 元素细分。

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

接下来,我们将详细探讨这些子部分。

LCP 分解

如果在 PageSpeed Insights 中没有显示改善此指标的方法,那么 LCP 优化可能是一项更复杂的任务。将复杂任务分解为易于处理的小任务并逐个解决通常更为有效。

本节将介绍如何将 LCP 分解为最重要的子部分,并阐述一套针对每个部分提供具体优化建议和最佳实践的方法论。

注意:有关本指南所讨论背景的概述,请参阅 Google I/O 2022 关于 LCP 优化的详细内容。

大多数页面加载通常涉及大量网络请求,但为了识别 LCP 的改进机会,我们应首先关注以下两个请求:

  • 初始 HTML 文档
  • LCP 资源(如果适用)

尽管页面上的其他请求也可能影响 LCP,但这两种请求(尤其是 LCP 资源的开始和结束时间)能够表明页面是否针对 LCP 进行了优化。

要识别 LCP 资源,可以使用开发者工具(如前面提到的 PageSpeed Insights、Chrome DevTools、WebPageTest 等)来确定 LCP 元素。然后,可以在所有已加载资源的网络瀑布图中,匹配由该元素加载的 URL(如果适用)。

例如,下面的可视化图表在一个典型页面加载的网络瀑布图中高亮了这些资源。在此示例中,LCP 元素的渲染需要一个图片请求。

显示网页 HTML 和 LCP 所需资源加载时间的瀑布图。

在优化良好的页面中,LCP 资源请求的加载应尽早开始,并且 LCP 元素应在 LCP 资源加载完成后尽快渲染。为了直观地判断特定页面是否符合此原则,我们可以将 LCP 总时间分解为以下几个子部分:

  • 首字节时间(TTFB):用户开始页面加载到浏览器接收到 HTML 文档响应第一个字节的时间。
  • 资源加载延迟:从 TTFB 到浏览器开始加载 LCP 资源的时间。如果 LCP 元素的渲染不需要加载资源(例如,使用系统字体渲染的文本节点),则该时间为 0。
  • 资源加载时间:LCP 资源本身加载所需的时间。如果 LCP 元素的渲染不需要加载资源,则该时间为 0。
  • 元素渲染延迟:从 LCP 资源加载完成到 LCP 元素完全渲染完毕的时间。

所有页面的 LCP 都由这四个子类别构成。它们之间没有重叠或间隙,总和即为 LCP 的总时间。

同一瀑布图,时间轴上叠加显示了四个 LCP 子类别。

所有页面的 LCP 值都可以归入这四个子部分。没有重叠或间隙,将它们相加即为 LCP 总时间。

在优化 LCP 时,分别优化这些子部分通常是有效的。但必须记住,我们需要优化所有部分。

在某些情况下,对某一部分应用的优化可能不会改善 LCP,而只是将节省的时间转移到另一部分。

例如,在前述网络瀑布图中,如果通过更大幅度的压缩或转换为更优格式(如 AVIF 或 WebP)来减小图片文件大小,那么资源加载时间会缩短,但 LCP 可能实际上并未改善,因为时间只是转移到了元素渲染延迟子部分。

资源加载时间缩短,但元素渲染延迟增加,LCP 并未减少。

这是因为在此页面中,LCP 元素在 JavaScript 代码加载完成之前是隐藏的,之后所有元素才一次性显示。

此示例表明,要优化 LCP 结果,需要优化所有这些子部分。

理想的子部分时间

要优化 LCP 的每个子部分,了解在优化页面中这些子部分理想的时间分配至关重要。

在这四个子部分中,有两个名称包含”延迟”一词。我们的目标是将这些时间尽可能接近零。另外两个部分涉及网络请求,本身就需要时间。

LCP 子部分 LCP 占比
首字节时间 约 40%
资源加载延迟 < 10%
资源加载时间 约 40%
元素渲染延迟 < 10%
总计 100%

这些时间划分是指导方针,而非严格规则。如果页面的 LCP 时间始终低于 2.5 秒,那么相对比例就不那么重要。然而,如果在”延迟”部分花费了不必要的时间,那么持续达到 2.5 秒的目标将变得非常困难。

在思考 LCP 时间的分解时,以下两点可供参考:

  • LCP 时间的大部分应花费在加载 HTML 文档和 LCP 源上。
  • 如果在 LCP 之前,这两个资源中的任何一个尚未加载完成,则表明存在改进空间。

警告:由于 LCP 的目标是 2.5 秒,您可能会想将这些百分比转换为绝对数值,但我们不建议这样做。这些子部分仅在相互关联时才有意义,因此我们建议始终以此方式进行衡量。

优化各部分的方法

了解了优化页面中 LCP 各子部分的时间分配后,就可以开始优化自己的页面。

以下四个部分将介绍优化每个子部分的建议和最佳实践。优化方案按效果大小降序排列。

1. 消除资源加载延迟

此步骤的目标是让 LCP 资源的加载尽可能早地开始。理论上,资源加载可以在 TTFB 之后立即开始,但实际上从 TTFB 到浏览器开始加载资源之间总是存在延迟。

作为一个经验法则,LCP 资源应与页面加载的第一个资源同时开始加载。如果 LCP 资源的加载开始时间晚于第一个资源,则存在改进空间。

在此页面中,LCP 资源的加载开始时间远晚于第一个加载的样式表。这一点存在改进空间。

通常,影响 LCP 资源加载开始时间的因素有两个:

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

优化资源被发现的时间

为了让 LCP 资源尽早开始加载,关键在于浏览器的预加载扫描器能在初始 HTML 文档响应中发现该资源。例如,在以下情况下,浏览器可以通过扫描 HTML 文档响应来发现 LCP 资源:

  • LCP 元素是 <img> 元素,并且其 srcsrcset 属性存在于初始 HTML 标记中。
  • LCP 元素需要 CSS 背景图片,并且该图片已通过 HTML 标记中的 <link rel="preload">(或 Link 头)进行了预加载。
  • LCP 元素是渲染需要网络字体的文本节点,并且该字体是通过 HTML 标记中的 <link>(或使用 Link 头)加载的。

以下是一些在扫描 HTML 文档响应时无法找到 LCP 资源的例子:

  • LCP 元素是通过 JavaScript 动态添加到页面中的 <img>
  • LCP 元素被使用 JavaScript 库延迟加载,这些库会隐藏 srcsrcset 属性(通常使用 data-src 或 `data-srcset)。
  • LCP 元素需要 CSS 背景图片。

在这些情况下,浏览器需要执行脚本或应用样式表,这通常需要等待网络请求完成。之后,才能发现 LCP 资源并开始加载。这并非最优做法。

为避免不必要的资源加载延迟,LCP 资源应能从 HTML 源代码中被发现。如果资源仅通过外部 CSS 或 JavaScript 文件引用,则应以高获取优先级对 LCP 资源进行预加载。例如:

<link rel="preload" as="image" href="hero-image.jpg">

警告:对于大多数页面来说,确保 LCP 资源与第一个资源同时开始加载就足够了。但是,也有可能构建出资源发现较晚,导致所有资源都在 TTFB 之后很久才开始加载。因此,虽然与第一个资源进行比较是识别改进机会的有效方法,但有时可能不够充分。这就是为什么测量此时间相对于 TTFB 的值并保持其较小的重要性所在。

优化资源被赋予的优先级

即使 LCP 资源可以从 HTML 标记中发现,其加载开始时间也可能不如第一个资源早。这可能发生在浏览器预加载扫描器的优先级启发式算法未能识别资源重要性,或判定其他资源更重要的情况下。

例如,在 <img> 元素上设置 loading="lazy" 可以延迟 LCP 图片的加载。延迟加载意味着在布局中确认图片进入视口之前,资源不会被加载,这可能导致加载开始时间延迟。

警告:请勿对 LCP 图片使用延迟加载。这总是会导致不必要的资源加载延迟,对 LCP 产生负面影响。

即使不使用延迟加载,由于图片是非渲染阻塞资源,它们永远不会被浏览器以最高优先级最先加载。您可以使用 fetchpriority 属性向浏览器提示哪些资源最重要。

对于页面中很可能成为 LCP 元素的 <img> 元素,建议设置 fetchpriority="high"。但是,如果为三个以上的图片设置高优先级,则优先级设置对减少 LCP 的帮助将减弱。

您也可以降低那些可能在文档响应早期出现,但因样式设置而最初不显示的图片的优先级(例如,在启动时不显示的轮播图幻灯片图片等)。

虽然降低特定资源的优先级可以释放更多带宽给需要的资源,但务必谨慎。始终在 DevTools 中检查资源的优先级,并在实验室工具和现场工具中测试更改。

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

LCP 资源现在与样式表同时开始加载。

关键点:LCP 资源即使可以从 HTML 源代码中发现,仍可能无法尽早开始加载的另一个原因是,该资源托管在另一个来源上。对于此类请求,浏览器需要在开始加载资源之前连接到该来源。如果可能,建议将关键资源托管在与 HTML 文档资源相同的来源。这样可以利用现有连接节省时间(稍后将详细讨论这一点)。

2. 消除元素渲染延迟

此步骤的目标是确保在 LCP 元素的资源加载完成后,能够立即渲染该元素。

LCP 元素无法在资源加载完成后立即渲染的主要原因包括:

  • 由于 <head> 中的样式表或同步脚本仍在加载,导致整个页面渲染被阻塞。
  • LCP 资源已加载完成,但 LCP 元素尚未添加到 DOM 中(等待 JavaScript 代码加载)。
  • 元素被其他代码隐藏,例如决定用户参与测试的 A/B 测试库。
  • 由于长任务导致主线程被阻塞,渲染工作必须等待长任务完成。

后续章节将讨论如何解决导致不必要元素渲染延迟的最常见原因。

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

通过 HTML 标记加载的样式表会阻塞其后所有内容的渲染。由于我们通常不希望渲染无样式的 HTML,这是期望的行为。然而,如果样式表过大,加载时间比 LCP 资源还长,那么即使 LCP 资源已加载完成,LCP 元素的渲染也会被阻塞。

图片和样式表的加载同时开始,但图片的渲染被阻塞,直到样式表准备就绪。

解决此问题可以执行以下任一操作:

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

通常,仅在样式表较小时才建议内联,因为 HTML 中的内联内容在后续页面加载中无法获得缓存的好处。如果样式表大到加载时间超过 LCP 资源,则可能不适合内联。

在大多数情况下,最佳解决方案是减小样式表的大小,使其小于 LCP 资源。这确保了在大多数访问中它不会成为瓶颈。

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

  • 移除未使用的 CSS:使用 Chrome DevTools 查找可能未使用并可移除(或延迟)的 CSS 规则。
  • 延迟加载非关键 CSS:将样式表拆分为页面首次加载所需的样式和可以延迟加载的样式。
  • 压缩和缩小 CSS:对于关键样式,尽可能减小传输大小。

延迟或内联渲染阻塞的 JavaScript

几乎没有必要将同步脚本(没有 asyncdefer 属性的脚本)添加到页面的 <head> 中,这样做通常会对性能产生负面影响。

如果在页面加载早期需要执行 JavaScript 代码,最好将其内联,以避免因等待额外网络请求而导致渲染延迟。然而,与样式表类似,仅当脚本非常小时才应内联。

<!-- 不应该这样做 -->
<script src="large-render-blocking-script.js"></script>

<!-- 应该这样做 -->
<script>
  // 将脚本内容直接内联在 HTML 中。
  // 重要:仅对非常小的脚本执行此操作。
</script>

使用服务器端渲染

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

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

  • 图片资源可以从 HTML 源代码中发现(如步骤 1 所述)。
  • 页面内容在渲染前无需完成额外的 JavaScript 请求。

SSR 的主要缺点是由于服务器处理时间增加,可能导致 TTFB 变慢。然而,通常这种权衡是值得的,因为我们可以控制服务器处理时间,但无法控制用户的网络和设备性能。

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

拆分长任务

即使遵循了上述建议,JavaScript 代码没有阻塞渲染,也不负责渲染元素,LCP 仍可能延迟。

这个问题最常见的原因是页面加载了需要在浏览器主线程上解析和执行的大型 JavaScript 文件。这意味着,即使图片资源已完全下载,渲染也可能必须等待无关脚本执行完成。

当前所有浏览器都在主线程上渲染图片。这意味着,任何阻塞主线程的因素都可能导致不必要的元素渲染延迟。

3. 缩短资源加载时间

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

  1. 减小资源大小。
  2. 缩短资源的传输距离。
  3. 减少网络带宽竞争。
  4. 完全消除网络时间。

警告:根据 Google 的研究,对于大多数网站来说,LCP 的加载时间部分往往不是主要的瓶颈。如果可能,请查看您网站的真实用户数据,以确定应重点关注哪些 LCP 子部分。

减小资源大小

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

缩短资源传输距离

除了减小资源大小,还可以通过将服务器尽可能靠近用户的地理位置来缩短加载时间。实现这一目标的最佳方法是使用内容分发网络(CDN)。

特别是图片 CDN 非常有用,因为它不仅能缩短资源传输距离,还能减小资源大小。图片 CDN 会自动实现前面提到的所有关于减小大小的建议。

关键点:虽然图片 CDN 是缩短资源加载时间的好方法,但使用第三方域名托管图片会产生额外的连接成本。虽然可以通过与来源的预连接来部分抵消此成本,但从相同来源提供图片仍然是最佳实践。许多 CDN 可以代理来自来源的请求。如果可用,这是一个值得考虑的绝佳选择。

减少网络带宽竞争

即使减小了资源大小并缩短了传输距离,如果同时还在加载许多其他资源,资源加载仍可能耗时。这个问题被称为网络竞争。

通过为 LCP 资源指定高的 fetchpriority 并尽早开始加载,浏览器会尽力确保低优先级资源不与 LCP 资源竞争。但是,如果您同时加载了大量高 fetchpriority 的资源,或者一般而言加载了大量资源,则可能会影响 LCP 资源的加载速度。

完全消除网络时间

缩短资源加载时间的最佳方法是从过程中完全消除网络。通过高效的缓存控制策略交付资源,对于第二次请求这些资源的用户,资源将从缓存中提供,使得资源加载时间几乎为零。

如果 LCP 资源是网络字体,除了减小其大小外,还应考虑网络字体资源加载是否需要阻塞渲染。将 font-display 设置为 autoblock 以外的值,可以确保文本在加载期间始终可见,并且 LCP 不会被额外的网络请求阻塞。

最后,如果 LCP 资源很小,可以将其内联为 data URL,从而避免额外的网络请求。但需要注意的是,使用 data URL 会阻碍资源的缓存,并可能产生额外的解码成本,从而导致渲染延迟更长。

4. 缩短首字节时间

此步骤的目标是尽快交付初始 HTML。此步骤通常也是开发者控制最少的,因此放在最后。然而,由于它直接影响所有后续步骤,所以也是最重要的步骤之一。

TTFB 变慢的一个常见原因是用户通过多个重定向(例如来自广告或短链接)访问网站。应始终将用户必须等待的重定向次数减至最少。

另一个常见原因是 CDN 边缘服务器无法使用缓存内容,必须将所有请求转发到源服务器。当访问者使用唯一的 URL 参数进行分析时,即使他们访问的是相同的页面,也可能发生这种情况。

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

使用 JavaScript 监控 LCP 分解

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

许多 RUM 产品已经使用这些 API 来计算子部分。web-vitals 库的属性构建版本也包含了这些 LCP 子部分的计时。有关如何在 JavaScript 中计算这些计时的具体方法,请参阅该库的代码。

Chrome DevTools 和 Lighthouse 也测量这些子部分,如前文截图所示,因此在使用这些工具时无需手动在 JavaScript 中进行计算。

总结

LCP 是复杂的,其计时可能受多种因素影响。然而,如果我们主要将其视为优化 LCP 资源加载的问题,就可以大大简化。

LCP 优化主要包含以下四个步骤:

  1. 确保 LCP 资源尽早开始加载。
  2. 确保 LCP 元素在其资源加载完成后能够立即渲染。
  3. 在保证质量的前提下,尽可能缩短 LCP 资源的加载时间。
  4. 尽快交付初始 HTML 文档。

如果您的页面能够遵循这些步骤,您就可以自信地说您正在为用户提供最佳的加载体验。同时,这也应该反映在您的实际 LCP 分数中。

标签: lcp

评论

发表评论

正在加载评论...