not a better man

前端技术

最大内容绘制(Largest Contentful Paint)译

原文链接:https://web.dev/largest-contentful-paint/

Largest Contentful Paint 这个指标让我们知道一个页面的重要的内容变得更加容易。

一直以来,对于web开发者来说,测量一个web页面的主要内容加载多快,渲染的速度多快,是个挑战。

老的测量指标,例如load DOMContentLoaded 并不算一个好的测量指标,这个指标并不能清楚的测量用户在屏幕上看到了什么。performance 中的First Paint(FTP) 和 First Contentful Paint(FCP) 仅仅只能捕获加载阶段的最初页面。如果一个页面正在展示启动页面,或者加载进度条,这个东西其实与用户无关。

我们之前推荐使用 peformanceAPI 中的First Meaningful Paint(FMP)Speed Index(SI)帮助我们了解初次渲染之后加载信息。但是这些指标复杂,难以解释,并经常出错,他们无法准确的描述页面的主要内容什么时候已经加载好

有时候越简单越好。基于在W3C Web Performance Working Group上的讨论,以及在Google做的研究。我们已经发现一个更准确的方式去测量一个页面的主要内容是否已经加载出来,该方式就是看页面的绝大部分元素是否已经渲染出来

Largest Contentful Paint 的定义

Largest Contentful Paint(LCP) API在chrome 77 中已经有了。 它用来指示最大内容元素能够在可视窗口显示出来,需要的渲染时间

哪些元素需要考虑在内呢?

一般来讲,Largest Contentful Paint 考虑到如下元素。

  • img 元素
  • 内嵌在svg中的image元素
  • video元素(使用到封面图片)
  • 拥有背景图片的元素(通过url()方式)
  • 包含文本节点或或行内文本节点的块级元素

注意,目前只是现在这些元素能够在模型建立之初保持模型简单,随着研究的深入,其他的元素元素也会被添加进来。

如何确定一个元素的大小?

为Largest Contentful Paint 所采用的元素的大小通常是用户在viewport中可见的大小。如果该元素超出可视区域,或者该元素不可见,或者该元素被裁剪,这些情况都不算该元素的尺寸。

对于图片元素来说,如果显示的大小和图片的固有尺寸不一样,如果图片原始尺寸比显示的图片尺寸小,那么图片元素的尺寸采取图原始尺寸,如果显示的尺寸比图片的原始尺寸小,那么报告的图片的显示尺寸。

对于文本元素来说,只需要考虑所有文本节点的大小(所有文本节点撑起来最小的矩形)

对于任何元素来说 通过css设置的margin ,padding, 或者border都不考虑在内

确定文本节点属于哪个元素有时候是件棘手的事情。尤其是该元素的包含了内联元素,文本节点,此外还有块级元素。问题的关键是每个文本节点都属于(并且仅属于)其最接近的块级祖先元素。

什么时候触发 largest contentful paint

网页通常是分阶段加载的,因此页面上的最大元素数量也可能会发生变化。怎么知道这些变化呢?浏览器会触发一个一个拥有largest-contentful-panit 类型的PerformanceEntry 。只要浏览器已经渲染了第一帧,就会触发。如果随后页面又进行了渲染。浏览器又会触发另一个PerformanceEntry 只要元素发生变化都会触发 。

我们需要注意的是,只有当某一个元素渲染出来,并且是可见的,该元素才被当做largest contentful element. 没有加载出来的图片不算,使用web fonts 文本节点在字体加载完成之前也不算。 在这种情况下,较小的元素可能会报告为最大的内容元素,但是较大的元素一旦完成渲染,就会通过另一个PerformanceEntry对象进行报告.

除了延迟加载图像和字体外,页面可能会向DOM中添加新元素。如果这些新元素中的任何一个大于先前的最大内容元素,那么还将将报告新的PerformanceEntry

如果页面从DOM中删除了一个元素,则不再考虑该元素。同样,如果元素的关联图像资源发生更改(例如,通过JavaScript更改img.src),则该元素将停止考虑,直到加载新图像为止。

当用户开始操作页面,(通过tap键,scroll,或点击键盘),浏览器将停止报告新的entry.

出于分析目的,您应仅向您的分析服务报告最近调度的PerformanceEntry。

注意由于用户可以在后台选项卡中打开页面,因此,只有在用户将选项卡聚焦后,才会有最大的绘画效果,这可能比他们第一次加载时晚。

加载时间 vs 渲染时间

因为安全原因,对于跨域图片来说,缺乏Time-Allow-Origin 头的图片是无法获取到渲染时间戳的。下面的展示获取图片1的的渲染时间戳并不合适。但是,如果有可能,我们还是推荐添加Time-Allow-Origin 头。这样我们会得到更精确的测量结果。

怎么处理元素布局和尺寸修改

为了降低计算,发布新的performance entry的开销,对元素大小或位置的更改不会生成新的LCP候选对象。LCP仅考虑元素的初始大小和在视口中的位置。

这意味着可能不会报告最初在屏幕外渲染然后在屏幕上过渡的图像。这也意味着最初在视口中渲染的元素随后被下推到视线外,仍然会报告其初始视口大小。

但是,如果一个元素从DOM中删除掉,或者关联的图片发送变化,将不会考虑

举例

下面举了一些热门网站什么时候出现Largest Contentful Paint的例子

在以上两个时间轴中,最大的元素随内容加载而变化。在第一个示例中,新内容被添加到DOM中,并且更改了最大的元素。在第二个示例中,布局发生了更改,以前最大的内容已从视口中删除。

通常情况下,延迟加载的内容要比页面上已经存在的内容大,但是这个无关紧要。接下来的两个示例显示了在页面完全加载之前发生的Largest Contentful Paint。

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

在Instagram时间轴的第一帧中,您可能会注意到相机徽标周围没有绿色框。那是因为它是一个<svg>元素,并且<svg>元素当前不被视为LCP候选对象。第一个LCP候选对象是第二个框架中的文本。

怎么测量LCP

我们1可以使用Largest Contentful Paint API来测量FCP .下面的例子展示创建PerformanceObserver 来监听 largest-contentful-panit 并将LCP 值打印在console 日志中

// Create a variable to hold the latest LCP value (since it can change).
let lcp;

// Create the PerformanceObserver instance.
const po = new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];

  // Update `lcp` to the latest value, using `renderTime` if it's available,
  // otherwise using `loadTime`. (Note: `renderTime` may not be available if
  // the element is an image and it's loaded cross-origin without the
  // `Timing-Allow-Origin` header.)
  lcp = lastEntry.renderTime || lastEntry.loadTime;
});

// Observe entries of type `largest-contentful-paint`, including buffered
// entries, i.e. entries that occurred before calling `observe()`.
po.observe({type: 'largest-contentful-paint', buffered: true});

// Send the latest LCP value to your analytics server once the user
// leaves the tab.
addEventListener('visibilitychange', function fn() {
  if (lcp && document.visibilityState === 'hidden') {
    console.log('LCP:', lcp);
    removeEventListener('visibilitychange', fn, true);
  }
}, true);

上面的例子只有当页面的生命周期变成hidden的时候,才会输出LCP的值.这是确保只输出最近一次LCP的一种方式。

怎么改善LCP

LCP 主要被三个因素影响

  • 服务器响应时间
  • CSS阻塞时间
  • 资源加载时间

此外,如果你么的站点是client渲染,我们的largest conetentful elements 是通过JavaScript添加到DOM中,这样脚本解析,执行时间也是LCP的一个因素。

下面有一些优化上述因素的指南

发表评论