乐趣区

关于web:页面视觉稳定性之优化CLS

最常见的影响 CLS 的分数的有:

  • 未指定尺寸的图片
  • 未指定尺寸的广告、嵌入元素、iframe
  • 动静插入内容
  • 自定义字体(引发 FOIT/FOUT)
  • 在更新 DOM 之前期待网络响应的操作

未指定尺寸的图片

总而言之:在 <img><video 标签上始终加上 widthheight 属性。或者,应用 CSS aspect ratio boxes 来占据空间。这种办法能够确保在图片加载过程中,浏览器能够调配足够的空间。

历史

在 web 的晚期,开发者会给 <img> 标签加上 widthheight 属性,以确保浏览器开始获取图片之前能够调配好空间,这样能够缩小 reflowre-layout

<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

你兴许会留神到这两个属性没有带单位。这些像素尺寸会确保保留 640 * 360 的区域。图片最终会平铺在这个区域,不论原始尺寸是否统一。

当响应式设计降临的时候,开发者开始疏忽 widthheight,开始应用 css 来调整图片大小。

img {
  width: 100%; /* or max-width: 100%; */
  height: auto;
}

这种办法的毛病是,只有图片下载的时候,浏览器才晓得图片的宽高并且调配好空间。图片下载完了,每张图片呈现在屏幕上的时候,页面都会 reflow 一次,会导致页面频繁的往下弹。这对于用户体验来说十分不敌对。

因而而诞生了 aspect ratioaspect ratio 是图片的宽高比。比方,x:y 的宽高比,指的是宽度 x 单位,高度 y 单位。

这也意味着只有咱们晓得宽高之一,就能计算出另一个属性。对于一个 16:9 的宽高比而言:

  • 如果图片有 360px 的高度,则宽度为 360 x (16 / 9) = 640px
  • 如果图片有 640px 的宽度,则高度为 640 x (9 / 16) = 360px

古代浏览器最佳体验

古代浏览器能够基于 widthheight 属性设定默认宽高比,这样就能防止布局偏移。开发者只须要如下设置:

<!-- set a 640:360 i.e a 16:9 - aspect ratio -->
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">
img {aspect-ratio: attr(width) / attr(height);
}

这样一来,图片加载之前,浏览器就能够依据宽高属性调配好空间。图片加载之后,就能够依据宽度或者高度属性,依照宽高比来调配理论空间。

图片的 aspect-ratio 属性在 chrome 和 firefox 上曾经能够应用了,safari 也快反对了。

如果图片位于容器内,能够设置宽度为容器宽度,高度为 auto,防止高度被固定位 360px。

img {
  height: auto;
  width: 100%;
}

响应式图片

在应用响应式图片的时候,srcset 定义了图片能够供浏览器抉择的尺寸。为了确保图片 widthheight 能够被设置,每张图片的宽高比必须统一。

<img width="1000" height="1000"
       src="puppy-1000.jpg"
    srcset="puppy-1000.jpg 1000w,
            puppy-2000.jpg 2000w,
            puppy-3000.jpg 3000w"alt="Puppy with balloons"/>

有时候咱们心愿展现图片的剪切局部,比方长图的两头正方形区域,为了视觉难看。

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg">
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg">
  <img src="puppy-800w.jpg" alt="Puppy with balloons">
</picture>

这样一来图片宽高比就不统一了,浏览器可能更须要针对每一个资源设置特定宽高比。但目前还没有好的解决方案,re-layout 仍然存在。

未指定尺寸的广告、嵌入元素、iframe

广告

广告是造成布局偏移的罪魁祸首之一。经常性,这些广告会有动静尺寸,这样会导致蹩脚的用户体验,当你在往下浏览页面的时候,广告忽然插入一些可见内容。

在广告的生命周期里,很多点能够导致布局偏移:

  • 广告容器插入到 dom 的时候
  • 本站代码调整广告容器尺寸的时候
  • 广告代码库加载的时候(导致容器尺寸扭转)
  • 广告内容填充容器的时候(如果最终广告的尺寸不一样,导致容器尺寸变动)

好消息是网站能够采纳最佳体验,来缩小布局偏移。

  • 为广告位动态保留空间。

    • 换句话说,在广告代码库加载之前,就给容器加好款式。
    • 如果要在内容流中插入广告,在插入之前确保通过保留尺寸来打消布局偏移。如果这些广告在屏幕外加载,则没有这个问题。
  • 在视图顶部插入非粘性广告的时候要特地留神。
  • 防止折叠预留的空间,如果广告没有返回,能够在该空间展现占位符。
  • 通过预留广告所需最大尺寸,来防止布局偏移。

    • 这很无效,不过如果广告很小,可能会有大片空白。
  • 依据历史数据,给广告加上适合的尺寸。

如果广告不太可能填满,一些网站会发现在初始的时候折叠广告位能够缩小布局偏移。很难做到每一次都能给广告位精准的尺寸,除非这个广告是你本人提供的。

为广告位动态保留空间

给广告容器设置固定的款式,防止代码库加载的时候,从新调整广告的尺寸。

要额定留神一下小尺寸的广告,如果预留很大的空间,会导致大片空白。

防止在视图顶部插入广告

依据 CLS 的计算规定,在顶部插入广告比在两头插入,造成的影响更大。

嵌入元素和 iframe

可嵌入的挂件能够容许你在页面上嵌入 web 内容(例如,youtube 视频、谷歌地图、社交媒体的帖子等)。这些嵌入元素能够采纳多种形式。

  • html fallback,而后 js 将该 fallback 转换成嵌入元素
  • 内联 html 代码块
  • iframe 嵌入

这些嵌入通常不会当时晓得嵌入的大小(例如,社交媒体帖子,是否蕴含图片?视频?或者多行文本?)。后果就是提供嵌入元素的平台常常无奈保障预留足够的空间,导致布局偏移。

为了应答这种状况,你能够通过提前计算嵌入元素的足够空间,以最小化 CLS。以下工作流能够参考:

  • 应用开发者工具查看最终嵌入的高度
  • 一旦嵌入元素加载,iframe 容器依据内容从新调整尺寸。

记下尺寸,并相应设置嵌入元素占位符的款式。你可能还会用到媒体查问来思考不同的因素。

动静内容

总而言之,防止在已存在的内容上方插入新内容,除非为了响应用户交互。这样能够保障任何布局偏移都是可预期的。

你可能常常会遇到从顶部或者底部弹出的一些内容。这常常产生在 banner 或者表单的中央,让页面的残余内容产生偏移。

  • “ 注册即可支付会员大礼包!”
  • “ 最近发表的文章 ”
  • “ 装置咱们的 APP”
  • “ 咱们还在承受订单 ”
  • “GDPR 提醒,是否容许应用 cookie”

如果你须要展现以上的 UI 内容,请提前预留好空间,防止产生布局偏移。

自定义字体(引发 FOIT/FOUT)

下载并渲染自定义字体会引发布局偏移,通过以下两种形式:

  • fallback 字体切换到新字体(FOUT – flash of unstyled text)
  • 从不可见变成可见,因为新字体的渲染缘故(FOIT – flash of invisible text)

以下工具能够帮你最小化影响:

  • font-display 属性能够让你批改自定义字体的渲染体现,通过应用可选值:auto, swap, block, fallbackoptional。可怜的是,除了 optional 之外的属性都会引发 re-layout,通过以上的其中一种形式。
  • Font Loading API 能够缩小获取必要字体的工夫。

Chrome 83 版本之后,能够采取以下计划:

  • 针对要害字体应用 <link rel=preload>,进步优先级,让字体下载有更高概率赶在 fcp 之前,这样就能防止布局偏移。
  • <link rel=preload>font-display: optional 联合应用。

动画

总而言之,优先思考 transform,而非会影响布局扭转的属性。

在更新 DOM 之前期待网络响应的操作

尽可能的在网络申请时,给一个 loading,或者占位符提醒,防止用户在这段时间内进行操作。

开发者工具

能够应用 lighthouse 和 performce 检测 CLS。

总结

  • 图片的尺寸,以及其余嵌入元素的尺寸,最开始就设定好,或者预留足够空间,这样能够无效防止布局偏移。
  • 利用图片宽高比的属性,能够在优化 CLS 的同时,做响应式布局。
  • 尽可能不要往已存在内容上方增加新内容。
  • web 字体尽可能早的加载,防止产生 FOIT 和 FOUT
  • 与 UI 共事配合在交互上防止布局偏移

参考

https://web.dev/optimize-cls/

退出移动版