识别和修复布局偏移的方法

作者:林语者 分类:工程代码

识别和修复布局偏移的方法

发布日期:2021年3月11日,最后更新日期:2025年2月7日

本文前半部分将介绍用于调试布局偏移的工具,后半部分将探讨识别布局偏移原因的思考方式。

工具

布局偏移可以通过 Layout Instability API 进行调试。此外,还可以使用像 DevTools 这样的工具,它们以更直观的方式汇总了该API的数据。

Layout Instability API

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

注意:Layout Instability API 仅在 Chromium 浏览器中受支持。目前,尚无在非 Chromium 浏览器中测量或调试布局偏移的方法。

用途

用于测量 Cumulative Layout Shift (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。

布局偏移信息通过结合 LayoutShift 和 LayoutShiftAttribution 两个接口进行报告。这些接口将在后续章节中详细说明。

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 显示在布局偏移期间移动的 DOM 元素列表。此数组最多可包含五个来源元素。如果受影响的元素超过五个,则根据对布局稳定性的影响程度报告前五个。该信息通过 LayoutShiftAttribution 接口报告(详见后文)。
value 报告特定布局偏移的得分。
hadRecentInput 指示布局偏移是否发生在用户输入后的500毫秒内。
startTime 指示布局偏移发生的时间。startTime 以毫秒为单位,相对于页面开始加载的时间进行测量。
duration 始终设置为 0。此属性继承自 PerformanceEntry 接口(LayoutShift 接口扩展了 PerformanceEntry 接口)。但由于布局偏移事件没有持续时间的概

LayoutShiftAttribution

LayoutShiftAttribution 接口描述了单个 DOM 元素的单次偏移。如果在一次布局偏移中有多个元素发生移动,sources 属性将包含多个条目。

例如,以下 JSON 对应一个由单个来源(一个 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,则表示元素移出了视口。

注意:Layout Instability API 不会忽略因被其他元素覆盖而不可见的元素。使用 display: nonevisibility: hiddenopacity: 0 可以在用户看到某些元素之前完成布局,从而避免布局偏移。

在解读这些输出时,最重要的一点是:列为 sources 的元素是在布局偏移期间发生移动的元素。然而,这些元素可能与布局不稳定的“根本原因”仅有间接关联。示例如下:

例 1 此布局偏移报告了一个来源:元素 B。但此布局偏移的根本原因是元素 A 的尺寸变化。

例 2 此例中的布局偏移报告了两个来源:元素 A 和元素 B。此布局偏移的根本原因是元素 A 的位置变更。

例 3 此例中的布局偏移报告了一个来源(元素 B)。由于元素 B 的位置变更导致了此布局偏移。

例 4 此例中,元素 B 的尺寸发生了变化,但并未发生布局偏移。

请查看 Layout Instability API 报告 DOM 变更方式的演示。

DevTools

DevTools 提供了多种工具来调试布局偏移。

Performance 面板

Performance 面板的实时指标视图允许您在操作页面时监控 CLS 得分,并识别导致布局偏移的操作。

Performance面板的实时指标视图可在页面操作期间监控网页的CLS得分。

一旦能够可靠地复现布局偏移,就可以进行跟踪以获取详细信息。

在Performance面板中记录新跟踪后,结果的”Layout Shifts”轨道会添加带有Layout Shift集群标记的紫色条。点击菱形可查看偏移动画,详细信息将显示在Summary面板中。

  • 布局偏移在 “Layout Shifts” 轨道中高亮显示。紫色线条将偏移分组到集群中,集群内的单个偏移用菱形表示。菱形的大小与偏移的大小成正比,因此您可以专注于最大的偏移。
  • 点击某个偏移会显示一个弹出窗口,其中包含偏移动画,发生偏移的元素会以紫色高亮显示。
  • Layout Shift 记录的 Summary 视图会显示开始时间、偏移得分和发生偏移的元素。这对于深入了解加载时的 CLS 问题特别有用,因为重新加载的性能分析很容易复现此类问题。
  • 它还会链接到左侧 “Insights” 面板中显示的 “Causes of layout shifts” 洞察。该洞察顶部显示总 CLS 以及布局偏移的可能原因。

有关如何使用 Performance 面板的详细信息,请参阅性能分析参考文档。

高亮显示布局偏移区域

高亮显示布局偏移区域可以快速了解页面上发生布局偏移的位置和时机。

要在 DevTools 中启用布局偏移区域高亮,请转到 Settings > More tools > Rendering > Layout Shift Regions,然后刷新要调试的页面。发生布局偏移的区域将以紫色高亮显示。

识别布局偏移原因的思考方式

无论布局偏移在何时或以何种方式发生,识别其根源通常可以遵循以下步骤。这些步骤可以通过运行 Lighthouse 来辅助,但请注意,Lighthouse 只能识别在初始页面加载期间发生的布局偏移。此外,Lighthouse 仅能对部分导致布局偏移的原因(例如没有明确宽度和高度的图片元素)提供建议。

识别布局偏移的原因

布局偏移可能由以下事件引发:

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

特别值得注意的是,发生偏移的元素之前的那个 DOM 元素,很可能是导致布局偏移的“原因”。因此,在调查布局偏移发生的原因时,请考虑以下几点:

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

如果前一个元素不是布局偏移的原因,请继续检查更前面的元素或邻近元素。

此外,从布局偏移的方向和距离中也能获得关于根本原因的线索。例如,大幅向下偏移通常表明有 DOM 元素被插入。而 1 或 2 像素的微小偏移,则可能暗示存在冲突的 CSS 样式被应用,或者网页字体正在加载和应用。

在此示例中,字体交换导致页面元素向上偏移了5像素。

导致布局偏移事件的最常见行为包括:

元素位置的变更(非由其他元素移动引起)

这通常由以下原因导致: - 延迟加载的样式表,或者覆盖了先前声明样式的样式表。 - 动画和过渡效果。

元素尺寸的变更

这通常由以下原因导致: - 延迟加载的样式表,或者覆盖了先前声明样式的样式表。 - 没有 widthheight 属性的图片和 iframe,在“占位槽”渲染完成后才加载。 - 没有设置 widthheight 的文本块在文本渲染后发生字体切换。

DOM 元素的插入或删除

这通常由以下原因导致: - 广告或其他第三方嵌入内容的插入。 - 横幅、警报框、模态框的插入。 - 无限滚动等用户体验模式,即在现有内容之上加载额外内容。

触发布局的动画

某些动画效果可能会触发布局。一个常见的例子是,通过递增 topleft 等属性来“动画化” DOM 元素,而不是使用 CSS 的 transform 属性。有关详细信息,请参阅如何创建高性能 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});

评论

发表评论

正在加载评论...