• #">什么是 CLS?#
    • #">什么是好的 CLS 分数?#
  • #">布局变化详细#
    • #">布局移位得分#
    • #">影响分数#
    • #">距离分数#
    • #">预期与意外的布局变化#
      • #">用户启动的布局转变#
      • #">动画和过渡#
  • #">如何测量 CLS #
    • #">现场工具#
    • #">实验室工具#
    • #">在 JavaScript 中测量 CLS #
      • #">指标和 API 之间的差异#
  • #">如何改进 CLS #
  • #">其他资源#

    2021 年 6 月 1 日: CLS 的实施发生了变化。要了解有关更改背后原因的更多信息,请查看演进 CLS 指标
    关键术语:累积布局偏移 (CLS) 是衡量视觉稳定性的一个重要的、以用户为中心的指标,因为它有助于量化用户体验意外布局偏移的频率——低 CLS 有助于确保页面令人愉悦
    当页面上的某些内容突然发生变化时,您是否曾经在线阅读过一篇文章?在没有警告的情况下,文本移动了,你就失去了位置。或者更糟糕的是:您正要点击一个链接或一个按钮,但在您的手指落地之前的瞬间——BOOM——链接移动,你最终点击了其他东西!
    大多数情况下,这些体验只是令人讨厌,但在某些情况下,它们可能会造成真正的伤害。
    说明布局不稳定如何对用户产生负面影响的截屏视频。
    页面内容的意外移动通常是因为资源是异步加载的,或者 DOM 元素被动态添加到现有内容上方的页面中。罪魁祸首可能是尺寸未知的图像或视频、呈现的字体大于或小于其后备,或者是动态调整自身大小的第三方广告或小部件。
    使这个问题更加成问题的是,网站在开发中的运作方式通常与用户体验的方式大不相同。个性化或第三方内容在开发中的表现通常与在生产中的表现不同,测试图像通常已经在开发人员的浏览器缓存中,并且本地运行的 API 调用通常如此之快以至于延迟不明显。
    累积布局偏移 (CLS) 指标通过测量真实用户发生的频率来帮助您解决此问题。

    什么是 CLS?#

    CLS 是衡量页面整个生命周期内发生的每个意外布局偏移的最大布局偏移分数的度量。
    布局移发生的任何时间的可见元素改变其从一个所再现的帧到下一个位置。(有关如何计算单个布局偏移分数的详细信息,请参见下文。)
    一连串的布局转换,称为会话窗口,是指一个或多个单独的布局转换快速连续发生,每次转换之间的时间少于 1 秒,整个窗口持续时间最多为 5 秒。
    最大的突发是具有该窗口内所有布局转换的最大累积分数的会话窗口。
    会话窗口示例。蓝条代表每个单独布局转变的分数。
    警告:以前,CLS 测量在页面的整个生命周期内发生的所有单独布局偏移分数的总和。要查看哪些工具仍然提供对原始实现进行基准测试的能力,请查看Web 工具中的 Evolving Cumulative Layout Shift

    什么是好的 CLS 分数?#

    为了提供良好的用户体验,网站应努力使 CLS 得分为0.1或更低。为确保您的大多数用户都能达到此目标,一个很好的衡量阈值是页面加载的第 75 个百分位数,在移动设备和桌面设备之间进行细分。
    image.png要了解有关此建议背后的研究和方法的更多信息,请参阅:定义 Core Web Vitals 指标阈值

    布局变化详细#

    布局偏移由Layout Instability API定义,它layout-shift会在视口中可见的元素在两帧之间更改其起始位置(例如,在默认书写模式下的顶部和左侧位置)时随时报告条目。这样的元素被认为是不稳定的元素
    请注意,仅当现有元素更改其起始位置时才会发生布局转换。如果将新元素添加到 DOM 或现有元素更改大小,则不会算作布局偏移 — 只要更改不会导致其他可见元素更改其起始位置。

    布局移位得分#

    为了计算布局偏移分数,浏览器会查看视口大小以及两个渲染帧之间视口中不稳定元素的移动。布局偏移分数是该运动的两个度量的乘积:影响分数距离分数(两者定义如下)。
    layout shift score = impact fraction * distance fraction

    影响分数#

    冲击部分措施如何不稳定因素影响两帧之间的视区。
    前一帧当前帧的所有不稳定元素的可见区域的联合(作为视口总面积的一部分)是当前帧的影响分数
    image.png
    在上图中,有一个元素在一帧中占据了视口的一半。然后,在下一帧中,元素向下移动视口高度的 25%。红色虚线矩形表示两个帧中元素可见区域的联合,在本例中,占总视口的 75%,因此其影响分数为0.75。

    距离分数#

    布局移动分数方程的另一部分测量不稳定元素相对于视口移动的距离。的距离分数是最大的距离的任何不稳定的元件在帧已经移动(水平或垂直任一)通过视域的最大尺寸(宽度或高度,取较大值)分开。
    image.png
    在上面的例子中,最大的视口尺寸是高度,不稳定元素移动了视口高度的 25%,这使得距离分数为 0.25。
    所以,在这个例子中,影响分数是0.75,距离分数是0.25,所以布局偏移分数是0.75 * 0.25 = 0.1875。
    最初,布局移位分数仅根据影响分数计算。的距离分数,是为了避免过于惩罚其中大元件由少量偏移的情况下。
    下一个示例说明向现有元素添加内容如何影响布局移位分数:
    image.png
    “点我!” 按钮附加到带有黑色文本的灰色框的底部,这会将带有白色文本的绿色框向下推(并部分移出视口)。
    在这个例子中,灰色框改变了大小,但它的起始位置没有改变,所以它不是一个不稳定的元素
    “点我!” 按钮以前不在 DOM 中,所以它的开始位置也不会改变。
    然而,绿色框的起始位置确实发生了变化,但由于它已部分移出视口,因此在计算影响分数时不考虑不可见区域。两个帧中绿色框的可见区域的联合(由红色虚线矩形表示)与第一帧中绿色框的区域相同 - 视口的 50%。该影响分数是0.5。
    距离分数被示为具有紫色箭头。绿色框向下移动了大约 14% 的视口,因此距离分数为0.14。
    布局移位得分为0.5 x 0.14 = 0.07。
    最后一个例子说明了多个不稳定元素
    image.png
    在上面的第一帧中,有四个 API 请求的动物结果,按字母顺序排序。在第二帧中,更多的结果被添加到排序列表中。
    列表中的第一项(“Cat”)不会在帧之间改变其起始位置,因此它是稳定的。同样,添加到列表中的新项目以前不在 DOM 中,因此它们的开始位置也不会改变。但是标有“狗”、“马”和“斑马”的物品都改变了它们的起始位置,使它们成为不稳定的元素
    同样,红色,点缀矩形表示这三者的联合不稳定因素前,后区“,在这种情况下是视域的面积(约38%的影响分数的0.38)。
    箭头表示不稳定元素从其起始位置移动的距离。由蓝色箭头表示的“Zebra”元素移动了最多,大约移动了视口高度的 30%。这就是本例中的_距离分数_0.3。
    布局移位得分为0.38 x 0.3 = 0.1172。

    预期与意外的布局变化#

    并非所有布局变化都是糟糕的。事实上,许多动态 Web 应用程序经常更改页面上元素的起始位置。

    用户启动的布局转变#

    如果用户没有预料到布局变化,它只会是不好的。另一方面,响应于用户交互(单击链接、按下按钮、在搜索框中键入等)而发生的布局转换通常没有问题,只要转换发生的交互足够接近关系是对用户一目了然。
    例如,如果用户交互触发可能需要一段时间才能完成的网络请求,最好立即创建一些空间并显示加载指示器,以避免在请求完成时出现令人不快的布局转换。如果用户没有意识到正在加载某些东西,或者不知道资源什么时候准备好,他们可能会在等待时尝试点击其他东西——可能会从他们下面移出的东西。
    在用户输入 500 毫秒内发生的布局转换将hadRecentInput设置标志,因此可以从计算中排除它们。
    注意:该hadRecentInput标志仅适用于离散输入事件,如点击、点击或按键。滚动、拖动或捏合和缩放手势等连续交互不被视为“最近输入”。有关更多详细信息,请参阅布局不稳定性规范

    动画和过渡#

    动画和过渡,如果做得好,是更新页面内容的好方法,不会让用户感到惊讶。页面上突然和意外移动的内容几乎总是会造成糟糕的用户体验。但是从一个位置逐渐自然地移动到下一个位置的内容通常可以帮助用户更好地了解正在发生的事情,并在状态变化之间引导他们。
    CSStransform属性允许您在不触发布局转换的情况下为元素设置动画:

    let sessionValue = 0;
    let sessionEntries = [];

    new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
    // Only count layout shifts without recent user input.
    if (!entry.hadRecentInput) {
    const firstSessionEntry = sessionEntries[0];
    const lastSessionEntry = sessionEntries[sessionEntries.length - 1];

    1. // If the entry occurred less than 1 second after the previous entry and<br /> // less than 5 seconds after the first entry in the session, include the<br /> // entry in the current session. Otherwise, start a new session.<br /> if (sessionValue &&<br /> entry.startTime - lastSessionEntry.startTime < 1000 &&<br /> entry.startTime - firstSessionEntry.startTime < 5000) {<br /> sessionValue += entry.value;<br /> sessionEntries.push(entry);<br /> } else {<br /> sessionValue = entry.value;<br /> sessionEntries = [entry];<br /> }
    2. // If the current session value is larger than the current CLS value,<br /> // update CLS and the entries contributing to it.<br /> if (sessionValue > clsValue) {<br /> clsValue = sessionValue;<br /> clsEntries = sessionEntries;
    3. // Log the updated value (and its entries) to the console.<br /> console.log('CLS:', clsValue, clsEntries)<br /> }<br /> }<br /> }<br />}).observe({type: 'layout-shift', buffered: true});<br />**警告**:<br />此代码显示了计算和记录 CLS 的基本方法。但是,以与[Chrome 用户体验报告](https://developers.google.com/web/tools/chrome-user-experience-report)(CrUX) 中衡量的内容相匹配的方式准确衡量 CLS更为复杂。详情请见下文:<br />在大多数情况下,页面卸载时的当前 CLS 值是该页面的最终 CLS 值,但有一些重要的例外:<br />以下部分列出了 API 报告的内容与指标计算方式之间的差异。

    指标和 API 之间的差异#

    • 如果页面是在后台加载的,或者在浏览器绘制任何内容之前已设置为背景,则不应报告任何 CLS 值。
    • 如果页面从后退/转发缓存中恢复,则其 CLS 值应重置为零,因为用户将其视为不同的页面访问。
    • API 不会报告layout-shiftiframe 内发生的班次的条目,但要正确衡量 CLS,您应该考虑它们。子框架可以使用 API 将它们的layout-shift条目报告给父框架进行聚合

    除了这些例外,CLS 还增加了一些复杂性,因为它测量页面的整个生命周期:

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

    为了处理这种情况,应该在页面处于后台的任何时候报告 CLS——除了它被卸载的任何时候(该visibilitychange事件涵盖这两种情况)。然后,接收此数据的分析系统将需要在后端计算最终的 CLS 值。
    开发人员可以使用web-vitalsJavaScript 库来衡量 CLS,而不是自己记住和解决所有这些情况,这说明了上述所有内容:
    import {getCLS} from ‘web-vitals’;

    // Measure and log CLS in all situations
    // where it needs to be reported.
    getCLS(console.log);
    您可以参考源代码以getCLS)获取有关如何在 JavaScript 中测量 CLS 的完整示例。
    在某些情况下(例如跨域 iframe),无法在 JavaScript 中测量 CLS。有关详细信息,请参阅库的限制部分web-vitals。

    如何改进 CLS #

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

    • 始终在您的图像和视频元素上包含大小属性,或者使用CSS 纵横比框之类的内容保留所需的空间。这种方法确保浏览器可以在加载图像时在文档中分配正确的空间量。请注意,您还可以使用unsized-media 功能策略在支持功能策略的浏览器中强制执行此行为。
    • 切勿在现有内容上方插入内容,除非响应用户交互。这可确保预期发生的任何布局变化。
    • 更喜欢转换动画而不是触发布局更改的属性动画。以提供从状态到状态的上下文和连续性的方式动画转换。

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

    其他资源#