【问题标题】:Cumulative Layout Shift with Bootstrap 4 gridBootstrap 4 网格的累积布局移位
【发布时间】:2020-12-31 07:01:23
【问题描述】:

在使用 Bootstrap (4.5) 网格进行两列布局并更改列顺序时,我遇到了高 CLS(内容布局移位)问题。

CLS 是 Core Web Vital 指标。基本上,当页面加载时网页的部分正在移动时,Google 会发现问题。据说这个指标会影响SEO。

在高分辨率上,我的布局由两列组成。右侧为主要内容,左侧为侧边栏。在较低分辨率下,侧边栏内容被推到主要内容下方。所以 HTML 看起来像这样:

<div class="container-fluid">
    <div class="row">
        <div class="col-lg-8 order-lg-2">
        </div>
        <div class="col-lg-4 order-lg-1">
        </div> 
    </div> 
</div>

问题在于,当页面在桌面上呈现的短暂时刻,主要内容出现在左侧,然后在几毫秒后它转移到右侧的正确位置。对于简单的页面(使用简单的 DOM 或没有外部资源),这种转变是不可察觉的。

我已准备好example of such page。 (source code is on github)。为了测量 CLS,我在 Chrome 中使用 Lighthouse。在我的情况下,当我刷新页面时,我可以看到列在移动,并且 Lighthouse 通知我 CLS 值为 0.326。结果可能取决于渲染资源,因此您可能会得到不同的结果。但似乎Google Page Insight 给出了类似的结果:

无论如何,有没有办法在页面呈现时避免这种转变?

【问题讨论】:

  • 我不知道为什么会发生这种情况(这似乎与当合成器操作发生排列层时有关,如果您切换顺序,它必须是“后期操作” ) 但解决方案是以正确的顺序提供 HTML,而不是依赖 CSS 来改变顺序。如果这对您来说是一个可行的选择,那么我会这样做,它会纠正问题,但显然如果顺序在其他屏幕尺寸上是正确的,这可能不起作用。有趣的是,如果我使用 CPU 减速,这不会发生,最好的猜测似乎是一个多线程问题。
  • 嗯,我们的想法是首先为单列布局(用于移动设备)提供主要内容。所以简单地改变顺序会带来新的问题。
  • 是的,这就是我的怀疑,我很感兴趣在页面上有图像、更多 CSS、JavaScript 等的“现实世界”场景中是否会出现同样的问题,因为额外的计算时间可能解决问题。如果问题仍然存在,您唯一能做的就是在页面加载之前显示白色叠加层,这将是一个 hack,但至少可以解决 CLS 问题。
  • 另外我想知道如果它从 flex 更改为布局的网格模型是否会出现同样的问题......就像我说的有趣的问题,因为 CSS 是内联的并且没有加载字体(我的东西通常会寻找 CLS 问题)
  • 问题出现在真实网站上。我已经对其进行了条纹化,所以没有人说这是其他问题。实际上,剥离资源使问题不易被发现。您提到制作叠加层,但我认为您无法在适当的时候轻松检测并删除它。

标签: css bootstrap-4 flexbox lighthouse pagespeed-insights


【解决方案1】:

似乎问题更多与 Chrome 相关,然后是 flexbox 或 bootstrap。事实证明,该问题是由过早渲染引起的。 Chrome 的解析器“产生”(因此它会触发渲染):

  • 读取 65536 字节数据后,
  • 在读取 50 个“令牌”(我认为基本上是 html 标签)后遇到&lt;script&gt; 标签后。

我提供的示例显示了第一种情况(但实际上我的真实网站经历了 CLS,因为第二种情况)。这两个案例都提交了与它们相关的“错误”:11302901041006

所以问题的答案是希望“错误”能够得到解决。同时根据实际原因,您可以限制文件大小或删除&lt;script&gt; 标签。

【讨论】:

  • 通过将我页面的所有各种&lt;script&gt; 块向下移动到页面底部的一个块,我解决了这个问题。
  • 这让我明白了。 (我没有使用引导程序)。 Pagespeed Insights 在页面看似无害的部分报告了一些令人震惊的累积布局偏移。 Chrome 开发工具性能选项卡在同一个 DOM 元素上调用了相同的内容。但是没有什么看起来像它应该导致 CLS 更不那么讨厌的一个。结果我在那个区域有一个很小的内联脚本标签(它所做的只是订阅一些页面事件)。我将它移到页面底部而不是在它的中间,现在不再报告 CLS 问题。
  • Chromium 正在改变解析行为,以便在此类情况下表现更好。新算法太大而无法适应这个余量,但本质上它将 65k 块增加到更大的值,并且在产生之前每个块将处理更多的令牌。在内联脚本之后,它还将处理比以前更多的令牌。有关更多详细信息,请参阅crbug.com/1041006
【解决方案2】:

简答

最小化 HTML,问题似乎就消失了。

更长的答案

我做了一些挖掘,这不是一个完整的“这就是发生的事情”的答案,但我有足够的想法提出上述解决方案并大致解释我的推理。我希望有人可以扩展我的知识差距。

那么是什么让我想出了上述解决方案?

分析页面加载后,我注意到创建了 2 个 HTML 解析任务。

一个处理 HTML 的第 1-770 行,另一个处理到文档末尾的第 771 行。

因此,页面似乎呈现了前 770 行,然后重新计算了第二个 HTML 解析任务的布局,因为您已经交换了顺序(并且 .col-lg-4 列在第二个解析过程 HTML 中)。

您不会在“正常”页面上看到这一点,就好像页面以 DOM 顺序呈现一样,无论如何布局都是正确的,第二个 HTML 解析过程只会为布局添加更多细节。

因为它在切断页面的位置看起来非常一致,所以我删除了所有换行符和空白。我的理论是,决定在哪里拆分 HTML 的任何算法都是使用行号作为计算的一部分。

通过将有效行数减少到 15 行左右,我希望使该算法仅一次解析 HTML。

它实际上仍然在 2 中进行,但最后一关是结束 &lt;/html&gt; 标记而已,所以没关系。这样做的结果是,当解析后的 H​​TML 与 CSSOM 结合时,它可以正确计算布局。

有点小技巧,但它应该可以达到特定的页面深度。

请注意 - 如果我将 DOM 节点数加倍,此解决方法将不再有效。如果我更改了每个列表项的长度(即放入 lorem ipsum)但没有更改结构,它没有任何区别。因此,似乎是 DOM 元素的数量和行号的某种组合决定了 HTML 解析器何时应该停止其第一次传递。

一个可能的解决方案

回到旧的布局模型。如果您使用 float:leftfloat:right 它应该可以工作。我认为这个具体问题是页面复杂性(DOM 节点数)和使用 flexbox 的结合。

由于 flexbox 比旧布局模型稍慢,并且有时不得不使用多次传递(旧布局模型是单次传递),我想这个问题不会在上述建议中持续存在。

Where I found out about multiple layout passes in certain scenarios

【讨论】:

  • 澄清一下,解析器根本不考虑行号。它主要使用令牌计数加上总体最大字节数来确定何时产生和执行渲染。
  • @MasonFreed 您是否碰巧有任何资源可以更详细地解释这一点,因为我想更好地理解这一点。谢谢。
  • 不幸的是,不,除了代码本身之外,我对此没有很好的参考。你可以在这里找到:source.chromium.org/chromium/chromium/src/+/main:third_party/…
猜你喜欢
  • 2021-05-28
  • 2021-07-07
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
  • 2022-07-23
  • 1970-01-01
  • 2019-04-14
  • 2022-11-12
相关资源
最近更新 更多