调试布局偏移

了解如何识别和修复布局偏移。

发布时间:2021 年 3 月 11 日,上次更新时间:2025 年 2 月 7 日

本文的第一部分讨论了用于调试布局偏移的工具,第二部分讨论了在识别布局偏移原因时要使用的思路。

工具

您可以使用 Layout Instability API 调试布局偏移,也可以使用 DevTools 等工具,这些工具以更易于理解的格式汇总来自此 API 的数据。

Layout Instability API

Layout Instability API 是浏览器用于衡量和报告布局偏移的机制。所有用于调试布局偏移的工具(包括 DevTools)最终都构建在 Layout Instability API 之上。但是,直接使用 Layout Instability API 是一种强大的调试工具,因为它具有灵活性。

用法

用于衡量累积布局偏移 (CLS)的相同代码代码段也可用于调试布局偏移。以下代码段将有关布局偏移的信息记录到控制台。检查此日志将为您提供有关布局偏移何时、何地以及如何发生的信息。

let cls = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});

运行此脚本时,请注意以下事项

  • buffered: true 选项表示 PerformanceObserver 应检查浏览器的性能条目缓冲区,以查找在观察者初始化之前创建的性能条目。因此,PerformanceObserver 将报告初始化之前和之后发生的布局偏移。在检查控制台日志时,请记住这一点。最初大量的布局偏移可能反映了报告积压,而不是突然发生大量布局偏移。
  • 为了避免影响性能,PerformanceObserver 会等到主线程空闲时再报告布局偏移。因此,根据主线程的繁忙程度,布局偏移发生的时间与控制台中记录的时间之间可能会有轻微延迟。
  • 此脚本会忽略在用户输入后 500 毫秒内发生的布局偏移,因此不计入 CLS。

有关布局偏移的信息是使用两个 API 的组合报告的:LayoutShiftLayoutShiftAttribution 接口。以下各节将更详细地解释每个接口。

LayoutShift

每个布局偏移都使用 LayoutShift 接口报告。条目的内容如下所示

duration: 0
entryType: "layout-shift"
hadRecentInput: false
lastInputTime: 0
name: ""
sources: (3) [LayoutShiftAttribution, LayoutShiftAttribution, LayoutShiftAttribution]
startTime: 11317.934999999125
value: 0.17508567530168798

之前的条目指示在布局偏移期间,三个 DOM 元素更改了位置。此特定布局偏移的布局偏移分数为 0.175

以下是 LayoutShift 实例的属性,这些属性与调试布局偏移最相关

属性 说明
sources sources 属性列出了在布局偏移期间移动的 DOM 元素。此数组最多可以包含五个来源。如果布局偏移影响的元素超过五个,则会报告布局偏移的五个最大来源(按对布局稳定性的影响衡量)。此信息使用 LayoutShiftAttribution 接口报告(在下面更详细地解释)。
value value 属性报告特定布局偏移的布局偏移分数
hadRecentInput hadRecentInput 属性指示布局偏移是否发生在用户输入后 500 毫秒内。
startTime startTime 属性指示布局偏移发生的时间。startTime 以毫秒为单位指示,并相对于页面加载启动的时间进行衡量。
duration duration 属性将始终设置为 0。此属性继承自 PerformanceEntry 接口(LayoutShift 接口扩展了 PerformanceEntry 接口)。但是,持续时间的概念不适用于布局偏移事件,因此设置为 0。有关 PerformanceEntry 接口的信息,请参阅规范

LayoutShiftAttribution

LayoutShiftAttribution 接口描述单个 DOM 元素的单个偏移。如果多个元素在布局偏移期间偏移,则 sources 属性包含多个条目。

例如,以下 JSON 对应于具有一个来源的布局偏移:<div id='banner'> DOM 元素从 y: 76 向下偏移到 y:246

// ...
  "sources": [
    {
      "node": "div#banner",
      "previousRect": {
        "x": 311,
        "y": 76,
        "width": 4,
        "height": 18,
        "top": 76,
        "right": 315,
        "bottom": 94,
        "left": 311
      },
      "currentRect": {
        "x": 311,
        "y": 246,
        "width": 4,
        "height": 18,
        "top": 246,
        "right": 315,
        "bottom": 264,
        "left": 311
      }
    }
  ]

node 属性标识偏移的 HTML 元素。在 DevTools 中将鼠标悬停在此属性上会突出显示相应的页面元素。

previousRectcurrentRect 属性报告节点的大小和位置。

  • xy 坐标分别报告元素的左上角的 x 坐标和 y 坐标
  • widthheight 属性分别报告元素的宽度和高度。
  • toprightbottomleft 属性报告与元素的给定边缘对应的 x 或 y 坐标值。换句话说,top 的值等于 ybottom 的值等于 y+height

如果 previousRect 的所有属性都设置为 0,则表示该元素已移入视图。如果 currentRect 的所有属性都设置为 0,则表示该元素已移出视图。

解释这些输出时,需要了解的最重要的事情之一是,列为来源的元素是在布局偏移期间偏移的元素。但是,这些元素可能仅与布局不稳定的“根本原因”间接相关。以下是一些示例。

示例 1

此布局偏移将报告一个来源:元素 B。但是,此布局偏移的根本原因是元素 A 的大小发生变化。

Example showing a layout shift caused by a change in element dimensions

示例 2

在此示例中,布局偏移将报告两个来源:元素 A 和元素 B。此布局偏移的根本原因是元素 A 的位置发生变化。

Example showing a layout shift caused by a change in element position

示例 3

在此示例中,布局偏移将报告一个来源:元素 B。更改元素 B 的位置导致了此布局偏移。

Example showing a layout shift caused by a change in element position

示例 4

尽管元素 B 的大小发生变化,但在此示例中没有布局偏移。

Example showing a element changing size but not causing a layout shift

查看Layout Instability API 如何报告 DOM 更改的演示

DevTools

DevTools 提供了许多工具来帮助调试布局偏移。

性能面板

性能面板的实时指标视图允许您与页面交互并监控 CLS 分数,以识别导致大型布局偏移的交互。

Layout Shift records being displayed in the live metrics screen of Chrome DevTools performance panel.
性能面板的实时指标视图允许在与网页交互时监控网页的 CLS 分数。

一旦您可以可靠地重现布局偏移,就可以执行跟踪以获取更多详细信息

Layout Shift records being displayed in the Chrome DevTools performance panel.
在性能面板中记录新跟踪后,结果的布局偏移轨道将填充紫色条,显示 Layout Shift 集群。点击菱形会显示偏移的动画以及摘要面板中的详细信息。

布局偏移在布局偏移轨道中突出显示。紫色线将偏移分组到偏移集群中,菱形显示该集群中的各个偏移。菱形的大小与偏移的大小成正比,使您可以专注于最大的偏移。

点击偏移会显示一个弹出窗口,其中包含偏移的动画,并以紫色突出显示元素偏移。

此外,Layout Shift 记录的摘要视图包括开始时间、偏移分数以及偏移的元素。这对于获取有关加载 CLS 问题的更多详细信息特别有帮助,因为这很容易通过重新加载性能配置文件来复制。

这也链接到左侧洞察分析面板中显示的布局偏移罪魁祸首洞察分析,其中显示了顶部的总 CLS,以及布局偏移的可能原因。

有关使用 性能 面板的更多信息,请参阅性能分析参考

突出显示布局偏移区域

突出显示布局偏移区域可能是一种有用的技术,可以快速、一目了然地了解页面上发生的布局偏移的位置和时间。

要在 DevTools 中启用“布局偏移区域”,请转到设置 > 更多工具 > 渲染 > 布局偏移区域,然后刷新要调试的页面。布局偏移区域将短暂地以紫色突出显示。

用于识别布局偏移原因的思路

无论布局偏移何时或如何发生,您都可以使用以下步骤来识别布局偏移的原因。这些步骤可以补充运行 Lighthouse - 但请记住,Lighthouse 只能识别初始页面加载期间发生的布局偏移。此外,Lighthouse 也只能为某些布局偏移原因提供建议 - 例如,没有显式宽度和高度的图像元素。

识别布局偏移的原因

布局偏移可能是由以下事件引起的

  • DOM 元素的位置更改
  • DOM 元素的尺寸更改
  • 插入或移除 DOM 元素
  • 触发布局的动画

特别是,紧接在偏移元素之前的 DOM 元素是最有可能与“导致”布局偏移相关的元素。因此,在调查布局偏移发生的原因时,请考虑

  • 前一个元素的位置或尺寸是否发生变化?
  • 是否在偏移元素之前插入或移除了 DOM 元素?
  • 是否显式更改了偏移元素的位置?

如果前一个元素没有导致布局偏移,请继续搜索,考虑其他前一个元素和附近的元素。

此外,布局偏移的方向和距离可以提供有关根本原因的提示。例如,较大的向下偏移通常表示插入了 DOM 元素,而 1 像素或 2 像素的布局偏移通常表示应用了冲突的 CSS 样式或加载和应用了 Web 字体。

Diagram showing a layout shift caused by a font swap
在此示例中,字体交换导致页面元素向上偏移了 5 个像素。

以下是一些最常导致布局偏移事件的特定行为

元素位置的更改(并非由于另一个元素的移动)

此类更改通常是以下原因的结果

  • 延迟加载或覆盖先前声明样式的样式表。
  • 动画和过渡效果。

元素的尺寸更改

此类更改通常是以下原因的结果

  • 延迟加载或覆盖先前声明样式的样式表。
  • 在呈现其“插槽”后加载的没有 widthheight 属性的图像和 iframe。
  • 在文本呈现后交换字体的没有 widthheight 属性的文本块。

DOM 元素的插入或移除

这通常是以下原因的结果

  • 插入广告和其他第三方嵌入。
  • 插入横幅广告、警报和模态框。
  • 无限滚动和其他 UX 模式,这些模式在现有内容之上加载其他内容。

触发布局的动画

某些动画效果可以触发布局。一个常见的例子是,当通过递增 topleft 等属性而不是使用 CSS 的 transform 属性来“动画化”DOM 元素时。阅读如何创建高性能 CSS 动画以获取更多信息。

重现布局偏移

您无法修复无法重现的布局偏移。要更好地了解您网站的布局稳定性,您可以做的最简单但最有效的事情之一是花 5-10 分钟与您的网站互动,目标是触发布局偏移。在执行此操作时保持控制台打开,并使用 Layout Instability API 报告布局偏移。

对于难以定位的布局偏移,请考虑使用不同的设备和连接速度重复此练习。特别是,使用较慢的连接速度可以更容易地识别布局偏移。此外,您可以使用 debugger 语句来更轻松地逐步调试布局偏移。

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      debugger;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});