优化LCP的超详细指南

2,624 阅读14分钟

最大内容的绘画LCP)是我最喜欢的核心网络活力。它是最容易优化的,也是三者中唯一一个在实验室里和在现场工作完全一样的(不要让我开始讨论这个问题......)。然而,令人惊讶的是,它是CrUX中优化程度最低的CWV--在撰写本文时,数据集中只有一半的起源有良好的LCP

我们再次看到,在良好的CLS的推动下,具有良好的核心网络生命力(CWV)的起源的数量有所增加。

52.7%的起源有良好的LCP
94.9%的起源有良好的FID
70.6%的起源有良好的CLS
39.0%的起源有良好的LCP、FID、和CLS

- Chrome用户体验报告📊(@ChromeUXReport)2022年3月8日

这确实让我吃惊,因为LCP是最简单的改进指标。因此,在这篇文章中,我想从一些非常简单的技巧开始,深入浅出地向你展示一些有趣的技巧和优化方法,以及一些陷阱和错误。

我们开始吧。

事先解决所有问题

让我们从简单的事情开始。LCP是一个里程碑式的计时--它测量...

...相对于页面第一次开始加载时,在视口内可见的最大图像或文本块的渲染时间。

这里需要注意的是,谷歌并不关心你是如何达到LCP的,只要你能快速到达那里。在页面加载生命周期的开始和它的LCP之间可能发生很多其他的事情。这些包括(但不限于)。

  • DNS、TCP、TLS协商
  • 重定向
  • TTFB
  • 第一次绘画
  • 第一批内容丰富的油漆

如果这些指标中的任何一个都很慢,你就已经处于落后状态了,而且它们会对你的LCP产生连锁反应。上述指标本身并不重要,但如果你能将它们尽可能降低,将有助于你的LCP。

Treo是一个令人难以置信的工具,可以从CrUX获得计时数据。

我对非技术性的利益相关者使用的一个比喻有点像这样。

你需要在08:30将孩子们送到学校。这就是学校所关心的--孩子们准时到校。你可以做很多事情来帮助实现这一点:在前一天晚上准备好他们的衣服;在前一天晚上准备好他们的午餐(你自己也要这样做)。设置适当的闹钟。有一个每个人都遵守的晨练程序。离开家时有足够的时间,并为交通问题等预留适当的缓冲时间。

学校并不关心你是否在前一天晚上布置了校服。评判你的标准是你是否有能力按时把孩子送到学校;尽你所能使之成为现实,这只是常识。

你的LCP也一样。谷歌(目前)并不关心你的TTFB,但一个好的TTFB会帮助你接近一个好的LCP。

优化整个链条。确保你事先尽可能快地得到一切,这样你就为成功做好了准备。

优化你的LCP候选者

一个希望不需要我说得太详细的提示:如果你有一个基于图像的LCP,确保它被很好地优化--合适的格式,适当的大小,合理的压缩,等等。不要用一张3MB的TIFF作为你的LCP候选人。

避免基于图像的LCPs

这对很多(如果不是大多数)网站来说是行不通的。但获得快速LCP的最好方法是确保你的LCP是基于文本的。这实际上是使你的FCP和LCP成为同义词12。就这样了。就这么简单。如果可能的话,避免基于图像的LCP候选人,而选择基于文本的LCP。

然而,这有可能对你不起作用。让我们来看看我们的其他选择。

使用最佳候选者

好的。现在我们开始进入有趣的东西。让我们看看我们有哪些LCP候选人,以及每个人是否有任何相对优势。

你的LCP有几个潜在的候选人。直接从web.dev的Largest Contentful Paint (LCP)页面上摘取,这些是:

  • <img> 元素
  • <image> <svg> 元素内的元素
  • <video> 元素(使用的是海报图片)
  • 一个带有背景图片的元素通过url()函数加载背景图片的元素(而不是CSS渐变)。
  • 含有文本节点或其他内联级文本元素子女的级元素。

演示

为了这篇文章的目的,我建立了一系列缩小的演示,展示了每个LCP类型的行为。每个演示都包含对一个阻塞性的<head> JavaScript文件的引用,以便。

  1. 夸大了瀑布,以及。
  2. 拖延解析器,看看每个LCP类型是否或如何受到预加载扫描器的影响。

值得注意的是,每个演示都是非常简单的,不一定代表现实情况,在这种情况下,许多响应会在同一时间飞行。一旦我们遇到资源争夺,LCP候选人的发现工作可能会与这些简化的测试案例中的表现不同。在这样的情况下,我们可能会寻求优先级提示预载来帮忙。我现在感兴趣的是浏览器如何处理某些资源的内在差异。

最初的演示可以在下面找到:

WebPageTest的比较可以让你看一下,尽管我们在文章后面会挑出个别的瀑布。这一切看起来是这样的。

注意报告中的LCP与<image><svg> :稍后会有更多关于这个的内容。(查看全尺寸。)

<img> 和 在LCP中是相同的poster<image> 在 中是第二快的<svg> ,尽管Chrome报告的LCP时间中有一个bug;background-image-based LCP明显是最慢的

Chrome浏览器≤101中的一个bug错误地将一个文本节点报告为LCP元素。这个问题在102版本中得到了修复。

正如我们所看到的,**并非所有的候选者都是天生平等的。**让我们更详细地看一下每一个。

<img> 元素

立即发现的LCP候选人。

在基于图像的LCP中,这可能是我们最喜欢的。<img> 元素,只要我们不把事情搞糟,就会很快被预加载扫描器发现,因此,可以与前面的--甚至是阻塞的--资源并行请求。

<picture> 而且<source />

值得注意的是,<picture> 元素的行为与<img /> 元素的行为相同。这就是为什么你需要为你的srcsetsizes 属性编写如此多的冗长的语法:其目的是为了给浏览器提供足够的图像信息,使其能够通过预加载扫描器请求相关文件,而不必等到布局。(虽然,我想从技术上讲,在计算使用<source />,srcset,sizes 的组合时,一定会有几毫秒的计算开销,但这很快就会被沿途的其他移动部分所取代)。

<image><svg>

<image> 在 中定义的元素显示了两个非常有趣的行为。第一个是一个简单的错误,即Chrome错误地报告了LCP候选者,似乎完全忽略了 。根据你的环境,这可能意味着更有利和更乐观的LCP分数。<svg> <image>

在写这篇文章的时候,Chrome ≤101有一个错误,报告的LCP回来时不是<image> 元素。在我们的演示中,它实际上被标记为更小的<p> 元素。

一旦在M102中进行修复(在撰写本文时是金丝雀,将在2022年5月24日达到稳定版),我们可以期待准确的测量。这确实意味着你的网站可能会遇到LCP得分下降的情况。

这个错误在Chrome 102中得到了修复。

由于目前的报告错误,<svg> 中的<image> 可能会从(无意中)最快的LCP类型之一,变成最慢的之一。如果你正在使用<svg> 中的<image> ,这可能是你想尽早检查的事情--你的分数可能会改变。

这个错误只涉及到报告的LCP候选人,并不影响浏览器实际处理资源的方式。<svg>为此,所有Chrome版本的瀑布看起来都是一样的,网络/调度行为也没有变化。这让我想到了我在<image> 中发现的第二个有趣的事情。

LCP候选人被隐藏在预加载扫描器中。

<image> 在 s中定义的元素似乎是从预加载扫描器中隐藏起来的:也就是说, 属性不会被解析,直到浏览器的主分析器遇到它。我只能猜测,这仅仅是因为预加载扫描器是为扫描HTML而不是SVG而设计的,这是个设计,而不是一个疏忽。也许Chrome可以做的一个优化是在HTML中预加载扫描嵌入式SVG......?但我相信这说起来容易做起来难......<svg> href

<video> 元素的 属性poster

我对<video>'sposter属性所表现出的行为感到惊喜。它的行为似乎与<img /> 元素相同,并且被预加载扫描器提前发现。

LCP候选人立即被发现。

这意味着poster LCPs本质上是相当快的,所以这是个好消息。

另一个消息是,如果没有poster ,似乎有意向将视频的第一帧作为LCP候选。这将是一个很难在2.5秒内完成的LCP,所以要么根本就没有<video> LCP,要么确保你开始使用poster 图像。

background-image: url();

当相关的DOM节点被解析时,LCP候选人被发现(这被同步JS阻断)。

在CSS中定义的资源(主要是通过url() 函数请求的任何东西)默认是缓慢的。这里最常见的候选者是背景图片和网页字体。

这些资源(在这个特定的例子中,背景图片)之所以慢,是因为它们是在浏览器准备绘制需要它们的DOM节点时才被请求的。你可以在这个Twitter主题中读到更多关于这个问题的内容。

所有开发者都应该记住的简单而重要的事情。CSS资源(字体、背景图片)不是由你的CSS来请求的,而是由需要它们的DOM节点来请求的[注:略显简化,但这是正确的思考方式。

- Harry Roberts (@csswizardry)2021年9月10

这意味着background-image LCPs是在最后一刻才被请求的,这就太晚了。我们不喜欢background-image LCPs。

绕过background-image 问题

如果你目前有一个网站的LCP是background-image ,你现在可能正在考虑重构或重建该组件。但是,令人高兴的是,有一个非常快速的解决方法,几乎不需要任何努力:让我们在后台补充一个隐藏的<img /> ,浏览器可以更早发现。

<div style="background-image: url(lcp.jpg)">
  <img src="lcp.jpg" alt="" width="0" height="0" style="display: none !important;" />
</div>

这个小黑客允许预加载扫描器拾取图像,而不是等到浏览器即将渲染<div> 。这比天真的background-image 实现快了1.058秒。你会注意到,这个瀑布几乎完全模仿了最快的<img /> 方案。

我们也可以preload ,而不是使用<img /> 元素,但我一般觉得preload 是有点代码味道的,应该尽可能避免。

总结

综上所述:

  • 基于文本的LCPs几乎总是最快的。
  • <img /> 而 LCPs是很好很快速的,可以通过预加载扫描仪发现。poster
  • <video> 在未来的Chrome版本中,如果没有 ,其第一帧可能会被视为LCP候选。poster
  • <image> 在 ,目前被误报,但速度很慢,因为 被隐藏在预加载扫描器中。<svg> href
  • background-image由于CSS的工作原理,默认情况下,s的速度很慢。
    • 我们可以通过添加一个不可见的<img /> ,来回避这个问题。

不要向自己的脚开枪

好了!现在我们知道了哪些是最好的选择,还有什么可以做的(或避免做的)以确保我们不会运行缓慢?事实证明,有很多事情是人们在无意中阻碍了LCP的得分。

不要偷懒装填你的LCP

每当我看到这一点,我的心就会下沉一点。懒惰地给你的LCP装子弹是完全违反直觉的。请不要这样做!

有趣的是,loading="lazy" 的一个特点是,它从预装扫描仪中隐藏了有关的图像。这意味着,即使图像在视口中,浏览器仍然会延迟请求它。

在我的测试中,懒散地加载我们的图像将LCP推回到4.418s。比<img /> 变体慢了1.274s,与background-image 测试几乎相同。

不要淡化你的LCP

可以预见的是,在500毫秒内淡入图像会将我们的LCP事件推后500毫秒。 Chrome浏览器将动画结束时间作为LCP测量时间,使我们的LCP事件为3.767秒,而不是3.144秒。

请注意,图像是在3.5秒时到达的,但LCP的报告是4秒。

避免在你的LCP候选人中出现褪色,无论它是基于图像还是文本。

不要把你的LCP托管在网站之外

在可能的情况下,我们应该总是自行托管我们的静态资产。这包括我们的LCP候选人。

网站所有者使用第三方图像优化服务(如Cloudinary)来提供自动和动态优化的图像并不罕见:即时调整大小、格式转换、压缩等。然而,即使考虑到这些服务的性能改进,前往不同产地的成本几乎总是超过了好处。在测试中,解决一个新产地的时间使下载我们的LCP图像的总时间增加了509ms。

通过各种方式,使用第三方服务来处理非关键的、非LCP的图像,但是如果你可以的话,把你的LCP候选人带到与主机页面相同的原点上。这正是我为这个网站所做的。

注意:虽然preconnect 可能有一点帮助,但它仍然不太可能比完全不打开一个新的连接更快。

不要把你的LCP建立在客户端上

我经常看到这种情况,这是对JavaScript持续迷恋的一部分。理想情况下,浏览器会收到你的HTML响应,而对LCP候选者的引用(最好是一个<img /> 元素)会立即出现在那里。然而,如果你用JS来构建你的LCP候选者,这个过程就会变得非常、非常冗长。

用JS建立你的LCP候选人,范围可以从一个简单的基于JS的图片库,一直到一个完全由客户渲染的页面。下面的瀑布图显示的是后者。

第一个反应是HTML。我们希望有一个<img /> ,就在标记中,等待着被发现,几乎是立即。相反,HTML在第12条请求一个deferedframework.js 。反过来,它最终在第50条请求关于当前产品的API数据。这个响应包含了有关产品图像的信息,这些信息最终作为一个<img /> ,放入虚拟DOM中,最终在第53条启动了对LCP候选者的请求,这时页面加载周期已经超过7秒。

不要篡改你自己的LCP

每次看到这个问题,我都很伤心......不要延迟加载任何意外地成为你的LCP候选者的内容。通常,这些都是像cookie横幅或通讯模版这样的东西,它们覆盖了内容并被标记为一个非常晚的LCP。我为我们的测试模拟了一个延迟加载的模态,重要的是要记住,这个分数是准确的,只是不是我们所希望的那样。

总结

好了。我们讲了很多,但收获很简单:基于文本的LCPs是最快的,但对大多数人来说不太可能实现。在基于图像的LCP类型中,**<img />poster**是最快的。<svg>中定义的<image>是很慢的,因为它们被隐藏在预加载扫描仪中。除此之外,还有几件我们需要避免的事情:不要懒于加载你的LCP候选者,不要用JS构建你的LCP