意外的布局偏移可能会在很多方面影响用户体验,例如,如果文本突然移动,导致用户在阅读时失去位置,或让用户点击错误的链接或按钮。在某些情况下,这可能会造成严重损害。
当以异步方式加载资源,或将 DOM 元素动态添加到网页中的现有内容之前时,通常会发生网页内容意外移动。导致布局偏移的原因可能包括尺寸未知的图片或视频、呈现的字体大于或小于其初始后备尺寸,或者是会自行动态调整大小的第三方广告或微件。
网站在开发中的运行方式与其用户体验之间的差异会让问题更严重。例如:
- 在开发阶段和生产环境中,个性化内容或第三方内容的行为往往有所不同。
- 测试图片通常已经在开发者的浏览器缓存中,但最终用户需要更长的时间来加载它们。
- 在本地运行的 API 调用速度通常非常快,以至于开发过程中察觉不到的延迟,可能会在生产环境中显著增加。
Cumulative Layout Shift (CLS) 指标可测量真实用户出现此问题的频率,从而帮助您解决此问题。
什么是 CLS?
CLS 衡量的是页面整个生命周期内发生的每次意外布局偏移的最大布局偏移分数爆发。
每当可见元素在渲染帧中发生位置改变时,就会发生布局偏移。(本指南的后面部分将详细介绍如何计算各个布局偏移分数。)
突发布局偏移称为会话窗口,是指一次或多次单独的布局偏移快速连续发生,且每次偏移之间间隔不到 1 秒,且整个窗口时长最多为 5 秒。
最大连拍是会话窗口,该窗口内所有布局偏移的累计得分最高。
CLS 得分怎样才算高?
为了提供良好的用户体验,网站应努力使 CLS 得分不超过 0.1。为确保大多数用户都能达到此目标,建议将网页加载的第 75 个百分位作为阈值衡量,并按移动设备和桌面设备细分。
如需详细了解此项建议所涉及的研究和方法,请参阅定义核心网页指标阈值。
布局偏移详情
布局偏移由 Layout Instability API 定义,该 API 会在视口内可见的元素在两帧之间更改起始位置(例如,在默认写入模式下的顶部和左侧位置)时报告 layout-shift
条目。此类元素被视为不稳定的元素。
请注意,仅当现有元素更改其起始位置时,才会发生布局偏移。如果向 DOM 添加了新元素或现有元素更改了大小,则不计为布局偏移,只要更改不会导致其他可见元素更改其起始位置即可。
布局偏移分数
为了计算“布局偏移分数”,浏览器会查看视口大小以及视口中两个渲染帧之间的不稳定元素移动情况。布局偏移分数是该移动的两种测量结果的乘积:影响分数和距离分数(两者均定义如下)。
layout shift score = impact fraction * distance fraction
影响比例
影响比例用于衡量不稳定的元素对两个帧之间的视口区域的影响。
指定帧的影响比例是该帧和上一帧中所有不稳定元素的可见区域的组合,以占视口总面积的比例表示。
![影响分数示例(包含一个不稳定元素)](https://web.developers.google.cn/static/articles/cls/image/impact-fraction-example-164341c82ee76.png?authuser=5&hl=sk)
在上图中,有一个元素在一帧中占据了视口的一半。然后,在下一帧中,元素将视口高度向下移动 25%。红色虚线矩形表示元素在两个帧中的可见区域的总和,在本例中为整个视口的 75%,因此其影响比例为 0.75
。
距离比例
布局偏移得分公式的另一部分用于衡量不稳定元素相对于视口移动的距离。距离比例是任何不稳定元素在帧中移动的最大水平或垂直距离除以视口的最大尺寸(宽度或高度,以较大者为准)。
![具有一个不稳定元素的距离分数示例](https://web.developers.google.cn/static/articles/cls/image/distance-fraction-example-9146d2a862482.png?authuser=5&hl=sk)
在前面的示例中,最大的视口尺寸是高度,且不稳定元素已移动了视口高度的 25%,这会导致距离比例为 0.25。
因此,在此示例中,影响比例为 0.75
,距离比例为 0.25
,因此布局偏移得分为 0.75 * 0.25 = 0.1875
。
示例
下例说明了向现有元素添加内容对布局偏移分数有何影响:
![包含多个稳定和_不稳定元素的布局偏移示例](https://web.developers.google.cn/static/articles/cls/image/layout-shift-example-mul-10d90b67b5d6a.png?authuser=5&hl=sk)
在此示例中,灰色框更改了大小,但其开始位置没有更改,因此它不是不稳定的元素。
“点击这里!”按钮之前不在 DOM 中,因此其开始位置也不会改变。
不过,绿色框的起始位置会发生变化,但由于其部分位置已移出视口,因此在计算影响比例时,系统不会考虑不可见区域。两个帧中绿色框的可见区域(用红色虚线矩形表示)与第一帧中绿色框的面积相同,即占视口的 50%。影响比例为 0.5
。
距离比例以紫色箭头表示。绿色框向下移动了大约 14% 的视口,因此距离比例为 0.14
。
布局偏移分数为 0.5 x 0.14 = 0.07
。
以下示例展示了多个不稳定元素如何影响网页的布局偏移得分:
![包含稳定和_不稳定元素_和视口裁剪的布局偏移示例](https://web.developers.google.cn/static/articles/cls/image/layout-shift-example-sta-05cf93597fcee.png?authuser=5&hl=sk)
在上图的第一帧中,有四个针对动物的 API 请求结果,按字母顺序排序。在第二帧中,排序后的列表中添加了更多结果。
列表中的第一项(“Cat”)不会在各帧之间更改它的起始位置,因此它是稳定状态。同样,添加到列表中的新内容之前不在 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 vitals
JavaScript 库会在 Web API 的限制范围内尽可能多地考虑这些因素。
指标与 API 之间的区别
- 如果页面在后台加载,或者页面在浏览器绘制任何内容之前置于后台,则它不应报告任何 CLS 值。
- 如果某个网页是从往返缓存中恢复的,则该网页的 CLS 值应重置为零,因为用户会将这种情况视为不同的网页访问。
- 该 API 不会报告 iframe 内发生的偏移的
layout-shift
条目,但该指标会报告网页用户体验的一部分。这可能会表现为 CrUX 和 RUM 之间的差异。若要正确衡量 CLS,您应予以考虑。子帧可以使用该 API 将其layout-shift
条目报告给父帧以进行聚合。
除了以下例外之外,CLS 会衡量网页的整个生命周期,因此会增加一些复杂性:
- 用户可能会长时间打开某个标签页,例如几天、几周、几个月。实际上,用户可能永远不会关闭标签页。
- 在移动操作系统上,浏览器通常不会针对后台标签页运行页面卸载回调,因此很难报告“最终结果”值。
为处理此类情况,每当网页在后台运行时,和卸载网页时都应报告 CLS(visibilitychange
事件涵盖了这两种情况)。然后,接收这些数据的分析系统将需要在后端计算最终的 CLS 值。
开发者不必自己记住并处理所有这些情况,而是可以使用 web-vitals
JavaScript 库来衡量 CLS,CLS 涵盖了上述所有情况(iframe 情况除外):
import {onCLS} from 'web-vitals';
// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);
<ph type="x-smartling-placeholder">
如何改善 CLS
如需获得更多指导信息,了解如何识别实际应用中的布局偏移并使用实验室数据进行优化,请参阅我们的优化 CLS 指南。
其他资源
- Google 发布商代码关于尽可能减少布局偏移的指南
- Understanding Cumulative Layout Shift(了解 Cumulative Layout Shift),作者:Annie Sullivan 和 Steve Kobes,#PerfMatters(2020 年)
更新日志
有时,错误是在用于衡量指标的 API 中发现的,有时是在指标本身的定义中发现的。因此,有时必须进行更改,这些更改可能会在内部报告和信息中心显示为改进或回归。
为帮助您应对此问题,对这些指标的实现或定义所做的所有更改都会显示在此更新日志中。
如果您对这些指标有反馈意见,可以在 web-vitals-feedback Google 群组中提供。