Cumulative Layout Shift (CLS)

Milica Mihajlija
Milica Mihajlija
Philip Walton
Philip Walton

浏览器支持

  • Chrome:77.
  • 边缘:79。
  • Firefox:不受支持。
  • Safari:不受支持。

来源

意外的布局偏移可能会以多种方式干扰用户体验,例如,如果文本突然移动,会导致用户在阅读时迷失位置,或者导致用户点击错误的链接或按钮。在某些情况下,这可能会造成严重损害。

布局突然发生变化,导致用户确认了原本打算取消的大型订单。

当资源异步加载或 DOM 元素在现有内容之前动态添加到网页时,网页内容通常会意外移动。布局偏移的原因可能是尺寸未知的图片或视频、比初始后备字体大或小的呈现字体,或者会自行调整大小的第三方广告或 widget。

网站在开发中的运行方式与其用户体验之间的差异会让问题更严重。例如:

  • 个性化内容或第三方内容在开发环境和生产环境中的行为通常有��不同。
  • 测试图片通常已位于开发者的浏览器缓存中,但对于最终用户,加载时间会更长。
  • 在本地运行的 API 调用通常非常快,因此在开发环境中不易察觉的延迟在生产环境中可能会变得非常明显。

累计布局偏移 (CLS) 指标可衡量真实用户发生此问题的频率,从而帮助您解决此问题。

什么是 CLS?

CLS 用于衡量在网页的整个生命周期内发生的每一次意外布局偏移的布局偏移得分的最高累计分数。

每当可见元素从一个呈现帧更改到下一个呈现帧时,都会发生布局偏移。(本指南稍后会详细介绍如何计算各个布局偏移分数。)

大量布局偏移(称为会话窗口)是指一个或多个单独的布局偏移快速连续发生,每次偏移之间间隔不到 1 秒,并且总窗口时长不超过 5 秒。

最严重的突发是指会话时段内所有布局偏移的累计分数最高的时段。

会话窗口示例。蓝条表示各个布局偏移的分数。

CLS 得分怎样才算理想?

为了提供良好的用户体验,网站应尽力使 CLS 得分不高于 0.1。为确保大多数用户都能达到此目标值,一个合适的衡量阈值是网页加载时间的第 75 个百分位数,并按移动设备和桌面设备进行细分。

CLS 值小于或等于 0.1 为良好,大于 0.25 为不佳,介于二者之间的值则需要改进
合适的 CLS 值为 0.1 或更低。差的值大于 0.25。

如需详细了解此建议背后的研究和方法,请参阅定义 Core Web Vitals 指标阈值

布局偏移详情

布局偏移由 Layout Instability API 定义,该 API 会在视口内可见的元素在两帧之间更改其起始位置(例如,在默认写入模式下的顶部和左侧位置)时报告 layout-shift 条目。此类元素被视为不稳定的元素

请注意,仅当现有元素更改其起始位置时,才会发生布局偏移。如果向 DOM 添加了新元素或现有元素更改了大小,则不会计为布局偏移,前提是更改不会导致其他可见元素更改其起始位置。

布局偏移分数

为了计算布局偏移得分,浏览器会查看两个渲染帧之间视口中的不稳定元素的大小和移动情况。布局偏移得分是该移动的两个测量值的乘积:影响百分比距离百分比(下文对这两个值进行了定义)。

layout shift score = impact fraction * distance fraction

撞击分数

影响比例用于衡量不稳定的元素对两个帧之间的视口区域的影响。

指定帧的影响比例是该帧和上一帧中所有不稳定元素的可见区域的组合,以占视口总面积的比例表示。

影响分数示例(包含一个不稳定元素)
如果某个元素的位置发生了变化,其之前和当前位置都会计入其影响比例。

在上图中,有一个元素在一帧中占据了视口的一半。然后,在下一帧中,元素将视口高度向下移动 25%。红色虚线矩形表示元素在两个帧中的可见区域的联合,在本例中,该区域占整个视口的 75%,因此其影响百分比0.75

距离比例

布局偏移得分公式的另一部分用于衡量不稳定元素相对于视口移动的距离。距离比例是任何不稳定元素在帧中移动的最大水平或垂直距离除以视口的最大尺寸(宽度或高度,以较大者为准)。

包含一个不稳定元素的距离分数示例
距离分数用于衡量元素在视口中的移动距离。

在前面的示例中,最大的视口尺寸是高度,且不稳定元素已移动了视口高度的 25%,这会导致距离比例为 0.25。

因此,在此示例中,影响分数0.75距离分数0.25,因此布局偏移得分0.75 * 0.25 = 0.1875

示例

以下示例展示了向现有元素添加内容如何影响布局偏移得分:

包含多个稳定元素和_不稳定元素_的布局偏移示例
在灰色框的底部添加一个按钮会将绿色框向下推,使其部分超出视口。

在此示例中,灰色方框会更改大小,但其起始位置不会更改,因此它不是不稳定的元素

“点击我!”按钮之前不在 DOM 中,因此其起始位置也不会发生变化。

不过,绿色框的起始位置确实会发生变化,但由于其部分位置已移出视口,因此在计算影响比例时,系统不会考虑不可见区域。两个帧中绿色框的可见区域(以红色虚线矩形表示)的并集与第一个帧中的绿色框的区域相同,即视口的 50%。影响分数0.5

距离分数用紫色箭头表示。绿色方框向下移动了约 14% 的视口,因此距离分数0.14

布局偏移分数为 0.5 x 0.14 = 0.07

以下示例展示了多个不稳定元素如何影响网页的布局偏移得分:

包含稳定元素和“不稳定元素”以及视口剪裁的布局偏移示例
随着此已排序列表中显示更多名称,现有名称会移动以保持字母顺序。

在上图的第一帧中,显示了针对动物的 API 请求的四个结果,这些结果按字母顺序排序。在第二帧中,排序后的列表中添加了更多结果。

列表中的第一项(“猫”)在帧之间不会更改其起始位置,因此是稳定的。同样,添加到列表的新项之前不在 DOM 中,因此���们的起始位置也不会发生变化。但是,标记为“狗”“马”和“斑马”的项都将其起始位置移到了其他位置,因此它们是不稳定的元素

同样,红色虚线矩形表示这三个不稳定元素的“前”和“后”区域的并集,在本例中,约占视口区域的 60%(0.60影响分数)。

箭头表示不稳定元素从其起始位置移动的距离。“斑马”元素(由蓝色箭头表示)移动了最多,移动了约 30% 的视口高度。因此,在此示例中,距离分数0.3

布局偏移分数为 0.60 x 0.3 = 0.18

预期布局偏移与意外布局偏移

并非所有布局偏移都是不好的。事实上,许多动态 Web 应用经常会更改页面中元素的起始位置。只有在用户未预料到的情况下,布局偏移才会造成不良影响。

用户发起的布局偏移

通常,在响应用户互动(例如点击或点按链接、按按钮或在搜索框中输入内容)时发生的布局偏移是可以接受的,前提是偏移发生的时间足够接近互动,以便用户清楚地了解二者之间的关系。

例如,如果用户互动触发了一个可能需要一段时间才能完成的网络请求,最好立即创建一些空间并显示加载指示器,以免在请求完成时出现令人不快的布局偏移。如果用户没有意识到内容正在加载,或者不知道资源何时可以准备就绪,他们可能会在等待期间尝试点击其他内容,这些内容可能会从其下方移出。

在用户输入后 500 毫秒内发生的布局偏移将设置 hadRecentInput 标志,以便从计算中排除。

动画和过渡

动画和转场效果如果处理得当,是更新网页内容且不会让用户感到意外的绝佳方式。网页上的内容突然意外转换几乎总会导致糟糕的用户体验。不过,如果内容从一个位置自然而然地逐渐移动到下一个位置,通常有助于用户更好地了解正在发生的情况,并在状态变化之间为用户提供引导。

请务必遵守 prefers-reduced-motion 浏览器设置,因为某些网站访问者可能会因动画而受到不良影响或注意力问题。

借助 CSS transform 属性,您可以为元素添加动画效果,而不会触发布局偏移:

  • 使用 transform: scale(),而不要更改 heightwidth 属性。
  • 如需移动元素,请避免更改 toprightbottomleft 属性,而改用 transform: translate()

如何衡量 CLS

CLS 可在实验室现��测量,并且可在以下工具中使用:

现场工具

实验室工具

在 JavaScript 中衡量布局偏移

如需在 JavaScript 中衡量布局偏移,请使用 Layout Instability API

以下示例展示了如何创建 PerformanceObserver 以将 layout-shift 条目记录到控制台:

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('Layout shift:', entry);
  }
}).observe({type: 'layout-shift', buffered: true});

在 JavaScript 中衡量 CLS

如需在 JavaScript 中衡量 CLS,您需要将这些意外的 layout-shift 条目划分到会话中,并计算会话的最大值。您可以参阅 web vitals JavaScript 库源代码,���中包含有关计算 CLS 方式的参考实现。

在大多数情况下,网页卸载时的当前 CLS 值是该网页的最终 CLS 值,但也有一些重要的例外情况,如下一部分所述。web vitals JavaScript 库会在 Web API 的限制范围内尽可能多地考虑这些因素。

指标与 API 之间的差异

  • 如果网页是在后台加载的,或者在浏览器绘制任何内容之前就进入后台,则不应报告任何 CLS 值。
  • 如果网页是从往返缓存中恢复的,则应将其 CLS 值重置为零,因为用户会将其视为一次不同的网页访问。
  • 对于在 iframe 中发生的转换,该 API 不会报告 layout-shift 条目,但该指标会报告,因为这些转换是网页用户体验的一部分。这可能会导致 CrUX 和 RUM 之间出现差异。为了准确衡量 CLS,您应考虑这些因素。子帧可以使用此 API 向父帧报告其 layout-shift 条目,以进行汇总

除了这些例外情况之外,由于 CLS 衡量的是网页的整个生命周期,因此还会带来一些额外的复杂性:

  • 用户可能会长时间(数天、数周、数月)让标签页保持打开状态。事实上,用户可能永远不会关闭标签页。
  • 在移动操作系统上,浏览器通常不会为后台标签页运行页面卸载回调,因此很难报告“最终”值。

为了处理此类情况,除了在页面卸载时报告 CLS 之外,还应在页面处于后台时报告 CLS(visibilitychange 事件涵盖这两种情况)。然后,接收此类数据的分析系统需要在后端计算最终的 CLS 值。

开发者无需自己记忆并处理所有这些情况,只需使用 web-vitals JavaScript 库来衡量 CLS,即可考虑上述所有情况(iframe 情况除外):

import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);

如何改善 CLS

如需有关在现场识别布局偏移并使用实验室数据进行优化的更多指导,请参阅我们的优化 CLS 指南。

其他资源

更新日志

有时,我们会在用于衡量指标的 API 中发现 bug,有时也会在指标本身的定义中发现 bug。因此,有时必须进行更改,这些更改可能会在内部报告和信息中心显示为改进或回归。

为帮助您应对此问题,对这些指标的实现或定义所做的所有更改都会显示在此更新日志中。

如果您对这些指标有反馈意见,可以在 web-vitals-feedback Google 群组中提供。