什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)

CLS是什么?为了提供良好的用户体验,网站应该努力将 CLS 分数控制在0.1 或以下。为了确保您能够在大部分用户的访问期间达成建议目标值,一个良好的测量阈值为页面加载的第 75 个百分位数,且该阈值同时适用于移动和桌面设备。

什么是 CLS?

测量整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移分数

每当一个可见元素的位置从一个已渲染帧变更到下一个已渲染帧时,就发生了布局偏移

一连串的布局偏移,也叫会话窗口,是指一个或多个快速连续发生的单次布局偏移,每次偏移相隔的时间少于 1 秒,且整个窗口的最大持续时长为 5 秒。

最大的一连串是指窗口内所有布局偏移累计分数最大的会话窗口。

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)

此前,CLS 测量的是整个页面生命周期内发生的所有单次布局偏移分数的总和。如需了解哪些工具仍然按照原方式提供测量功能,请查看网络工具集中不断发展的累积布局偏移 。

怎样算是良好的 CLS 分数?

为了提供良好的用户体验,网站应该努力将 CLS 分数控制在0.1 或以下。为了确保您能够在大部分用户的访问期间达成建议目标值,一个良好的测量阈值为页面加载的第 75 个百分位数,且该阈值同时适用于移动和桌面设备。

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)

如需详细了解这些建议值背后的研究和方法论,请参阅:定义核心 Web 指标的指标阈值

布局偏移详情

布局偏移由布局不稳定性 API定义,只要可视区域中可见元素的起始位置(例如,元素在默认书写模式下的顶部和左侧位置)在两帧之间发生了变更,该 API 就会报告 layout-shift 条目。这样的元素被视为不稳定元素

请注意,只有当现有元素的起始位置发生变更时才算作布局偏移。如果将新元素添加到 DOM 或是现有元素更改大小,则不算作布局偏移,前提是元素的变更不会导致其他可见元素的起始位置发生改变。

布局偏移分数

浏览器在计算布局偏移分数时,会查看可视区域大小和两个已渲染帧之间的可视区域中不稳定元素的位移。布局偏移分数是该位移的两个度量的乘积:影响分数距离分数(两者定义如下)。
布局偏移分数 = 影响分数 * 距离分数

影响分数

影响分数测量不稳定元素对两帧之间的可视区域产生的影响。

前一帧当前帧的所有不稳定元素的可见区域集合(占总可视区域的部分)就是当前帧的影响分数

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)-2

在上图中,有一个元素在一帧中占据了一半的可视区域。接着,在下一帧中,元素下移了可视区域高度的 25%。红色虚线矩形框表示两帧中元素的可见区域集合,在本示例中,该集合占总可视区域的 75%,因此其影响分数为0.75 。

距离分数

布局偏移分数计算公式的另一部分测量不稳定元素相对于可视区域位移的距离。距离分数指的是任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)-3

在上方的示例中,最大的可视区域尺寸维度是高度,不稳定元素的位移距离为可视区域高度的 25%,因此距离分数为 0.25。

所以,在这个示例中,影响分数是0.75 ,距离分数是0.25 ,所以布局偏移分数是0.75 * 0.25 = 0.1875 。

最初,布局偏移分数只根据影响分数进行计算。引入距离分数是为了避免在大元素发生微小位移的情况下进行过度惩罚的情况。

下一个示例说明了向现有元素添加内容对布局偏移分数的影响:

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)-4

"点我!"按钮附加在了包含黑色字体文本的灰色框底部,将带有白色字体文本的绿色框下推(且一部分已在可视区域外)。

在这个示例中,灰色框的大小改变了,但起始位置没有改变,所以该灰色框不是一个不稳定元素

"点我!"按钮一开始并不在 DOM 中,所以它的起始位置也没有改变。

然而,绿色框的起始位置确实发生了变化,但由于绿色框的一部分已经在可视区域外,因此在计算影响分数时不考虑不可见区域。两帧中绿色框的可见区域集合(由红色虚线矩形框表示)与第一帧中绿色框的区域相同,为可视区域的 50%。影响分数为0.5 。

距离分数由紫色箭头表示。绿色框向下移动的距离大约为可视区域的 14%,因此距离分数为0.14 。

布局偏移分数是0.5 x 0.14 = 0.07 。

最后一个示例说明了多个不稳定元素的情况:

什么是CLS?Cumulative Layout Shift 累积布局偏移 (CLS)-5

在上面的第一帧中,有四个 API 请求的动物名称结果,按首字母顺序进行了排序。在第二帧中,更多的结果被添加到排序列表中。

列表中第一项("Cat")的起始位置没有在两帧之间发生改变,因此是稳定的。同样,添加到列表中的新项目之前不在 DOM 中,因此这些项目的起始位置也没有改变。但"Dog"、"Horse"和"Zebra"这几项的起始位置都发生了改变,因此都是不稳定元素

同理,红色虚线矩形框代表这三个不稳定元素前后两帧的区域集合,在本示例中,大约占可视区域面积的 38%(影响分数为0.38 )。

箭头表示不稳定元素相对于起始位置的位移距离。由蓝色箭头表示的"Zebra"元素位移的距离最大,约为可视区域高度的 30%。因此本示例中的距离分数为0.3 。

布局偏移分数是0.38 x 0.3 = 0.1172 。

预期布局偏移 vs. 意外布局偏移

布局偏移并不总是坏事。事实上,许多动态网络应用程序经常更改页面元素的起始位置。

由用户发起的布局偏移

布局偏移只有在用户并不期望其发生时才算是坏事。换言之,对用户交互(单击链接、点选按钮、在搜索框中键入信息等)进行响应的布局偏移通常没有问题,前提是偏移发生的时机与交互时机足够接近,使用户对前后关系一目了然。

例如,如果某次用户交互触发了一个网络请求,而该请求可能需要一段时间才能完成,那么最好立即留出一些空间并显示加载指示器,避免在请求完成时出现令用户不快的布局偏移。如果用户没有意识到当前正在加载某些内容,或者不知道资源什么时候能够准备就绪,他们就可能会在等待期间尝试点击其他内容(来打破僵局)。

在用户输入 500 毫秒内发生的布局偏移会带有hadRecentInput标志,便于在计算中排除这些偏移。

注意:hadRecentInput标志仅适用于不连续输入事件,如轻触、点击或按键操作。滚动、拖动或捏拉缩放手势等连续性交互操作不算作"最近输入"。更多详情请参阅布局不稳定性规范

动画和过渡

动画和过渡如果做得好,确实是一个在更新页面内容时不让用户感到突兀的好方法。页面内容突然发生意外偏移几乎无一例外会造成糟糕的用户体验。但如果内容从一个位置逐步又自然地移动到下一个位置,这通常有助于用户更好地明白当前状况,并引导他们适应状态之间的变化。

CSS transform属性使您能够在不触发布局偏移的情况下为元素设置动画:

  • 用transform: scale()来替代和调整height和width属性。
  • 如需使元素能够四处移动,可以用transform: translate()来替代和调整top、right、bottom或left属性。

如何测量 CLS

CLS 可以进行实验室测量或实际测量,并且可以在以下工具中使用:

注意:实验室工具通常在一个合成环境中加载页面,因此只能测量页面加载期间发生的布局偏移。因此,实验室工具为某一特定页面报告的 CLS 值可能低于真实用户的实际体验值。

实测工具

实验室工具

在 JavaScript 中测量 CLS

要在 JavaScript 中测量 CLS,您可以使用布局不稳定性 API。以下示例说明了如何创建一个PerformanceObserver来侦听意外layout-shift条目、将条目按会话分组、记录最大会话值,并在最大会话值发生改变时更新记录。
let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// 只将不带有最近用户输入标志的布局偏移计算在内。
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// 如果条目与上一条目的相隔时间小于 1 秒且
// 与会话中第一个条目的相隔时间小于 5 秒,那么将条目
// 包含在当前会话中。否则,开始一个新会话。
if (sessionValue &&
entry.startTime - lastSessionEntry.startTime < 1000 &&
entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
sessionValue = entry.value;
sessionEntries = [entry];
}
// 如果当前会话值大于当前 CLS 值,
// 那么更新 CLS 及其相关条目。
if (sessionValue > clsValue) {
clsValue = sessionValue;
clsEntries = sessionEntries;
// 将更新值(及其条目)记录在控制台中。
console.log('CLS:', clsValue, clsEntries)
}
}
}
}).observe({type: 'layout-shift', buffered: true});

警告:上述代码说明了计算和记录 CLS 的基本方法。但是,要想准确测量 CLS,使之与Chrome 用户体验报告 (CrUX) 中的测量值相匹配,那么测量方式要更为复杂。详情请见下文:

在大多数情况下,页面卸载时的当前 CLS 值就是该页面的最终 CLS 值,但有一些重要的例外:

以下部分列出了 API 报告的内容与指标计算方式之间的差异。

指标和 API 之间的差异

  • 如果页面是在后台加载的,或者页面在浏览器绘制任何内容之前被转移到后台,那么该页面不应该报告任何 CLS 值。
  • 如果页面通过往返缓存恢复,则该页面的 CLS 值应重置为零,因为这对用户来说是多次不同的页面访问体验。
  • API 不会基于 iframe 中的偏移来报告layout-shift条目,但要想正确测量 CLS,您应该考虑这些偏移。子框架可以使用 API 将这些偏移的layout-shift条目报告给父框架来进行聚合

由于 CLS 的测量对象是整个页面生命周期,所以除了上述这些例外,该指标还有一些额外的复杂性:

  • 用户可能会在长一段时间内(几天、几周、几个月)让一个选项卡保持打开状态。事实上,用户可能永远不会关闭该选项卡。
  • 在移动操作系统上,浏览器通常不会为后台选项卡运行页面卸载回调,因为这样很难报告"最终"值。

为了应对这些情况,只要当页面处于后台时就应该报告 CLS,页面卸载时也是如此(visibilitychange事件涵盖这两种情况)。而接收该数据的分析系统则需要在后端计算最终的 CLS 值。

开发者不必记住所有这些情况并独自应付,而是可以使用web-vitals JavaScript 库来测量 CLS,库会自行处理上述所有情况:

import {getCLS} from 'web-vitals';
// 在所有需要汇报 CLS 的情况下
// 对其进行测量和记录。
getCLS(console.log);

您可以参考(getCLS)的源代码,了解如何在 JavaScript 中测量 CLS 的完整示例。
在某些情况下(例如跨域 iframe),CLS 无法在 JavaScript 中进行测量。详情请参阅web-vitals库的局限性部分。

如何改进 CLS

对于大多数网站来说,您可以通过遵循一些指导原则来避免所有的意外布局偏移:

  • 始终在您的图像和视频元素上包含尺寸属性,或者通过使用CSS 长宽比容器之类的方式预留所需的空间。这种方法可以确保浏览器能够在加载图像期间在文档中分配正确的空间大小。请注意,您还可以使用unsized-media 功能策略在支持功能策略的浏览器中强制执行此行为。
  • 除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容。这样能够确保发生的任何布局偏移都在预期之内。
  • 首选转换动画,而不是触发布局偏移的属性动画。动画过渡的目标是提供状态与状态之间的上下文连续性。

如需深入了解如何改进 CLS,请参阅优化 CLS调试布局偏移

评分

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注