意外的布局偏移可能会以多种方式破坏用户体验,例如,如果文本突然移动,会导致用户在阅读时迷失位置,或者导致用户点击错误的链接或按钮。在某些情况下,这可能会造成严重损害。
当资源异步加载或 DOM 元素在现有内容之前动态添加到网页时,网页内容通常会意外移动。布局偏移的原因可能是尺寸未知的图片或视频、比初始后备字体大或小的呈现字体,或者会自行调整大小的第三方广告或 widget。
网站在开发过程中的运作方式与用户体验到的运作方式之间的差异会加剧这一问题。例如:
- 个性化内容或第三方内容在开发环境和生产环境中的行为通常有所不同。
- 测试图片通常已在开发者的浏览器缓存中,但对于最终用户,加载时间会更长。
- 在本地运行的 API 调用通常非常快,因此在开发环境中不易察觉的延迟在生产环境中可能会变得非常明显。
累计布局偏移 (CLS) 指标可衡量真实用户发生此问题的频率,从而帮助您解决此问题。
什么是 CLS?
CLS 用于衡量在网页的整个生命周期内发生的每一次意外布局偏移的布局偏移得分的最高累计分数。
每当可见元素从一个呈现帧更改到下一个呈现帧时,都会发生布局偏移。(本指南稍后会详细介绍如何计算各个布局偏移分数。)
大量布局偏移(称为会话窗口)是指一个或多个单独的布局偏移快速连续发生,且每次偏移之间的间隔时间不到 1 秒,并且总窗口时长不超过 5 秒。
最严重的突发是指会话时段内所有布局偏移的累计分数最高的时段。
CLS 得分怎样才算理想?
为了提供良好的用户体验,网站应尽力使 CLS 得分不高于 0.1。为确保大多数用户都能达到此目标值,一个合适的衡量阈值是网页加载时间的第 75 个百分位数,并按移动设备和桌面设备进行细分。
如需详细了解此建议背后的研究和方法,请参阅定义 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()
,而不要更改height
和width
属性。 - 如需移动元素,请避免更改
top
、right
、bottom
或left
属性,而改用transform: translate()
。
如何衡量 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 API 的限制范围内,web vitals
JavaScript 库会尽可能考虑这些因素。
指标与 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 指南。
其他资源
- Google 发布商代码关于尽可能减少布局偏移的指南
- Annie Sullivan 和 Steve Kobes 在 #PerfMatters 大会 (2020) 上发表的“了解累积布局偏移”演讲
更新日志
有时,我们会在用于衡量指标的 API 中发现 bug,有时也会在指标本身的定义中发现 bug。因此,有时必须进行更改,这些更改可能会在内部报告和信息中心中显示为改进或回归。
为帮助您管理这些指标,我们会在此更新日志中显示对这些指标的实现或定义所做的所有更改。
如果您对这些指标有任何反馈,可以通过 web-vitals-feedback Google 群组提供反馈。