解决布局不稳定问题

使用 WebPageTest 识别和修复布局不稳定性问题的演示。

在早些时候的一篇博文中,我介绍了如何在 WebPageTest 中衡量 Cumulative Layout Shift (CLS)。CLS 是所有布局偏移的集合,因此在本博文中,我认为有兴趣深入探究和检查网页上的每次布局偏移,以尝试了解可能导致不稳定性的原因,并真正尝试解决问题。

测量布局偏移

使用 Layout Instability API,我们可以获取页面上所有布局偏移事件的列表:

new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
  }).observe({type: "layout-shift", buffered: true});
}).then(console.log);

这会生成一个没有输入事件的布局偏移数组:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 210.78500000294298,
    "duration": 0,
    "value": 0.0001045969445437389,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

在此示例中,在 210 毫秒处发生了一次非常细微的偏移,为 0.01%。

了解转移的时间和严重程度有助于缩小可能导致转移的原因的范围。我们回头使用 WebPageTest,在实验室环境中进行更多测试。

在 WebPageTest 中衡量布局偏移

与在 WebPageTest 中测量 CLS 类似,衡量单个布局偏移也需要自定义指标。幸运的是,由于 Chrome 77 已是稳定版本,这一过程变得更轻松。Layout Instability API 默认处于启用状态,因此您应该能够在 Chrome 77 的任意网站上执行该 JS 代码段,并立即获得结果。在 WebPageTest 中,您可以使用默认的 Chrome 浏览器,而无需担心命令行 flag 或使用 Canary 版。

因此,我们来修改该脚本,为 WebPageTest 生成一个自定义指标:

[LayoutShifts]
return new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
  }).observe({type: "layout-shift", buffered: true});
});

此脚本中的 promise 将解析为数组的 JSON 表示法,而非数组本身。这是因为自定义指标只能生成字符串或数字等原始数据类型。

我要测试的网站是 ismyhostfastyet.com,这是我为了比较网站主机的实际加载性能而构建的网站。

确定布局不稳定的原因

results 中,我们可以看到 LayoutShifts 自定义指标具有以下值:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

总而言之,在 3087 毫秒处发生的单次布局偏移为 34.2%。为了找出问题根源,我们将使用 WebPageTest 的幻灯影片视图

幻灯影片中的两个单元格,显示布局偏移前后的屏幕截图。
幻灯影片中的两个单元格,显示布局偏移前后的屏幕截图。

滚动到幻灯影片中约 3 秒的标记,就能清楚地看到导致 34% 布局偏移的原因:彩色表格。该网站异步提取 JSON 文件,然后将其呈现到表格中。该表最初是空的,因此在结果加载后等待表填满会导致偏移。

网页字体标题突然出现。
网页字体标头突然出现。

但还不止这些。如果网页的显示时间大约为 4.3 秒,我们可以看到网页“Is my host quick yet?”的 <h1> 突然出现。之所以发生这种情况,是因为该网站使用了网页字体,并且尚未采取任何措施来优化呈现效果。发生这种情况时,布局实际上并没有发生变化,但是必须等待这么长时间才能看到标题仍然会导致糟糕的用户体验。

解决布局不稳定问题

既然我们已经知道异步生成的表格会导致三分之一的视口偏移,现在是时候解决这个问题了。在实际加载 JSON 结果之前,我们不知道表的内容,但我们仍然可以使用某种占位符数据填充表,以便布局本身在 DOM 渲染时相对稳定。

以下是生成占位符数据的代码:

function getRandomFiller(maxLength) {
  var filler = '█';
  var len = Math.ceil(Math.random() * maxLength);
  return new Array(len).fill(filler).join('');
}

function getRandomDistribution() {
  var fast = Math.random();
  var avg = (1 - fast) * Math.random();
  var slow = 1 - (fast + avg);
  return [fast, avg, slow];
}

// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
  var [fast, avg, slow] = getRandomDistribution();
  window.data.push({
    platform: getRandomFiller(10),
    client: getRandomFiller(5),
    n: getRandomFiller(1),
    fast,
    avg,
    slow
  });
}
updateResultsTable(sortResults(window.data, 'fast'));

占位符数据在排序之前是随机生成的。它包含“█”字符随机重复几次,以便为文本创建视觉占位符,以及随机生成的三个主要值分布。我还添加了一些样式,降低表格中所有颜色的饱和度,以清楚地表明数据尚未完全加载。

您使用的占位符的外观对布局稳定性无关紧要。使用占位符的目的是向用户保证,内容即将显示,网页没有损坏。

以下是加载 JSON 数据时占位符的外观:

数据表呈现的是占位数据。
数据表以占位数据呈现。

解决网页字体问题要简单得多。由于网站使用的是 Google Fonts,我们只需在 CSS 请求中传入 display=swap 属性即可。仅此而已。Fonts API 将在字体声明中添加 font-display: swap 样式,使浏览器能够立即以后备字体渲染文本。以下是包含修复程序的相应标记:

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

验证优化

通过 WebPageTest 重新运行页面后,我们可以生成前后对比,以直观呈现差异并衡量布局不稳定性的新程度:

WebPageTest 幻灯影片,显示并排加载和不使用布局优化的两个网站。
WebPageTest 幻灯影片,显示两个网站在启用和不优化布局的情况下如何并排加载。
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

根据自定义指标,在 3071 毫秒(与之前大致相同)时发生布局偏移,但偏移的严重程度得多:0.005%。我可以接受。

幻灯片中也可以很清楚,<h1> 字体会立即回退到系统字体,以便用户更快阅读。

总结

复杂网站可能会出现比本示例更多的布局偏移,但修复过程仍然是相同的:向 WebPageTest 添加布局不稳定性指标,通过直观的加载幻灯影片交叉引用相应结果,以找出问题根源,然后使用占位符保留屏幕空间,从而修复问题。

(还有一件事)衡量真实用户遇到的布局不稳定性

能够在优化前后对网页运行 WebPageTest,并查看某个指标是否有所提升,但真正重要的是用户体验真的在不断改善。这难道不是我们想要先去完善网站的原因吗?

因此,如果我们开始衡量真实用户的布局不稳定体验以及传统网页性能指标,将会是一次非常棒的选择。这是优化反馈环中至关重要的一环,因为从现场获得数据可以让我们了解问题所在,以及修正是否产生了积极影响。

除了收集您自己的布局不稳定性数据以外,您还可以参阅 Chrome 用户体验报告,其中包含来自数百万个网站真实用户体验的 Cumulative Layout Shift 数据。通过它,您可以了解自己(或竞争对手)的表现,也可以用它来探索整个网络上布局不稳定的状态。