了解如何避免突然的布局偏移,从而改善用户体验
Cumulative Layout Shift (CLS) 是三个核心网页指标指标之一。该指标会结合视口中可见内容的偏移量和受影响元素移动的距离,来衡量内容的不稳定性。
布局偏移可能会分散用户的注意力。假设您在开始阅读一篇文章时突然元素在网页内四处移动,导致您离开页面,需要重新找到相应位置。这种情况在网络上非常常见,包括在阅读新闻或尝试点击“搜索”或“添加到购物车”按钮时。此类体验会造成视觉冲击和失望。通常,由于可见元素因突然添加到页面中或调整大小而被迫移动,就会导致这些错误。
为了提供良好的用户体验,网站应努力使至少 75% 的网页访问的 CLS 不超过 0.1。
其他核心网页指标是基于时间的值(以秒或毫秒为衡量依据),而 CLS 得分是无单位的值,根据计算的内容偏移量和移动幅度计算得出。
在本指南中,我们将介绍如何优化导致布局偏移的常见原因。
导致 CLS 不佳的最常见原因包括:
- 没有尺寸的图片。
- 广告、嵌入和没有尺寸的 iframe。
- 动态注入的内容,例如广告、嵌入式内容和无尺寸的 iframe。
- 网络字体。
了解布局偏移的原因
在开始查看常见 CLS 问题的解决方案之前,请务必了解您的 CLS 得分以及变化的根源。
实验室工具与现场使用的 CLS
开发者常常听到开发者认为 Chrome 用户体验报告 (CrUX) 衡量的 CLS 不正确,因为 CLS 与他们使用 Chrome 开发者工具或其他实验室工具衡量的 CLS 不一致。Lighthouse 等网站性能实验室工具可能不会显示网页的完整 CLS,因为它们通常会执行简单的网页加载来衡量某些网页性能指标并提供一些指导(不过,Lighthouse 用户流确实可以在默认网页加载审核之外进行衡量)。
CrUX 是 Web Vitals 计划的官方数据集,因此,系统会在网页的整个生命周期内衡量 CLS,而不只是在实验室工具通常衡量的初始网页加载期间进行衡量。
布局偏移在网页加载期间非常常见,因为系统会获取所有必要的资源来首次呈现网页,但初始加载后也可能会发生布局偏移。许多加载后偏移可能由用户互动导致,因此不会计入 CLS 得分,因为它们是预期的偏移,但只要在互动发生后的 500 毫秒内发生偏移。
不过,在没有符合条件的互动的情况下(例如,您沿网页继续滚动并且延迟加载的内容加载了导致偏移),也可能会包含用户意料之外的其他加载后偏移。导致加载后 CLS 的其他常见原因是转换交互,例如在单页应用中,这超过了 500 毫秒的宽限期。
PageSpeed Insights 的“了解真实用户遇到了什么情况”部分会显示用户感知的来自某个网址的 CLS,“诊断性能问题”部分会显示基于实验室的负载 CLS。 这些值之间的差异可能是由于加载后 CLS 造成的。
![PageSpeed Insights 的屏幕截图,其中突出显示了实际用户 CLS(明显大于 Lighthouse CLS)的网址级数据](https://web.developers.google.cn/static/articles/optimize-cls/image/screenshot-pagespeed-ins-1b9715cccc402.png?authuser=9&hl=lv)
确定负载 CLS 问题
如果 PageSpeed Insights 的 CrUX 和 Lighthouse CLS 得分基本一致,这通常表示 Lighthouse 检测到了加载 CLS 问题。在本例中,Lighthouse 将帮助进行两项审核,以提供有关因缺少宽度和高度而导致 CLS 的图片的更多信息,还会列出因网页加载而变化的所有元素及其 CLS 贡献。您可以通过按 CLS 审核进行过滤来查看这些审核:
![Lighthouse 屏幕截图:显示了 CLS 审核结果,提供了更多信息来帮助您识别和解决 CLS 问题](https://web.developers.google.cn/static/articles/optimize-cls/image/lighthouse-screenshot-sho-1c6eeefdc4b1b.png?authuser=9&hl=lv)
开发者工具中的“Performance”面板也会在体验部分中突出显示布局偏移。Layout Shift
记录的 Summary 视图包含累计布局偏移得分,以及显示受影响区域的矩形叠加层。这对于获取关于加载 CLS 问题的更多详细信息尤为有用,因为可以通过重新加载性能配置文件轻松复制该问题。
![当展开“体验”部分时,“布局偏移”会记录 Chrome 开发者工具性能面板中的显示情况](https://web.developers.google.cn/static/articles/optimize-cls/image/layout-shift-records-bein-90d4c6c924a25.jpg?authuser=9&hl=lv)
Layout Shift
记录。点击记录即可显示详细信息(例如此图片中的“已移至”和“已移至”条目),深入了解受影响的元素。
确定加载后 CLS 问题
CrUX 和 Lighthouse CLS 得分不一致通常表示加载后 CLS。如果没有现场数据,跟踪这些变化可能会比较困难。如需了解如何收集现场数据,请参阅衡量现场的 CLS 元素。
Web Vitals Chrome 扩展程序可用于在您与网页互动时监控 CLS,无论是通过平视显示模式,还是在控制台中,您可以详细了解偏移的元素。
除了使用此扩展程序,您还可以在浏览网页的同时使用粘贴到控制台的 Performance Observer 记录布局偏移。
设置轮班监控后,您可以尝试重现任何加载后 CLS 问题。CLS 通常发生在用户滚动浏览网页时,即延迟加载的内容已完全加载,但没有预留空间。用户将指针悬停在内容上时内容偏移是加载后 CLS 的另一个常见原因。在上述任一互动过程中,任何内容偏移都会被视为意外,即使发生在 500 毫秒内。
如需了解详情,请参阅调试布局偏移。
在确定导致 CLS 的常见原因后,您还可以使用 Lighthouse 的时间跨度用户流模式,确保典型的用户流不会因引入布局偏移而回归。
测量现场的 CLS 元素
在确定在哪些情况下发生 CLS 并缩小可能的原因范围时,现场监控 CLS 非常有用。与大多数实验工具一样,现场工具仅测量发生变化的元素,但通常能够提供足够的信息来确定原因。您还可以使用 CLS 字段衡量结果来确定哪些问题最具有优先级,以便解决。
web-vitals
库具有归因函数,可让您收集这些额外的信息。如需了解详情,请参阅在实际应用中调试性能。其他 RUM 提供商也已开始以类似方式收集和呈现这些数据。
导致 CLS 的常见原因
确定导致 CLS 的原因后,您就可以开始着手解决问题了。在本部分中,我们将展示一些导致 CLS 的常见原因,以及您可以采取哪些措施来避免这些错误。
没有尺寸的图片
务必在图片和视频元素中添加 width
和 height
尺寸属性。或者,通过 CSS aspect-ratio
或类似工具预留所需的空间。此方法可确保浏览器能够在图片加载期间分配正确的文档空间。
![Lighthouse 报告显示了在图像上设置尺寸后对 Cumulative Layout Shift 之前/之后的影响](https://web.developers.google.cn/static/articles/optimize-cls/image/lighthouse-report-showing-9556bbb060b37.png?authuser=9&hl=lv)
图片的 width
和 height
属性的历史记录
在 Web 发展初期,开发者会将 width
和 height
属性添加到其 <img>
标记中,以确保在浏览器开始提取图片之前在网页上分配足够的空间。这会最大限度地减少自动重排和重新布局。
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">
此示例中的 width
和 height
不包含单位。这些“像素”尺寸可确保浏览器在网页布局中预留 640x360 的区域。无论实际尺寸是否与图片相匹配,图片都会拉伸以适应该空间。
在引入自适应设计后,开发者开始忽略 width
和 height
,转而使用 CSS 来调整图片大小:
img {
width: 100%; /* or max-width: 100%; */
height: auto;
}
但是,由于未指定图片大小,因此在浏览器开始下载图片并确定其尺寸之前,无法为其分配空间。图片加载时,文本会在页面中下移,为图片腾出空间,从而造成令人困惑且令人失望的用户体验。
这正是宽高比的用武之地。图片的宽高比是指其宽度与高度的比率。这种情况通常以由英文冒号分隔的两个数字表示(例如 16:9 或 4:3)。对于 x:y 的宽高比,图片宽度为 x 单位,高为 y 单位。
也就是说,如果我们知道其中一个维度,就可以确定另一个维度。宽高比为 16:9:
- 如果 puppy.jpg 的高度为 360 像素,则宽度为 360 x (16 / 9) = 640 像素
- 如果 puppy.jpg 的宽度为 640px,高度为 640 x (9 / 16) = 360px
知道图片的宽高比后,浏览器就能计算高度和相关区域并预留足够的空间。
设置图片尺寸的新最佳实践
由于现代浏览器会根据图片的 width
和 height
属性设置图片的默认宽高比,因此您可以通过在图片上设置这些属性并在样式表中添加前面的 CSS 来防止布局偏移。
<!-- set a 640:360 i.e a 16:9 aspect ratio -->
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">
之后,所有浏览器都会根据元素的现有 width
和 height
属性来添加默认宽高比。
这将在图片加载前根据 width
和 height
属性计算宽高比。它会在布局计算开始时提供这些信息。一旦系统告知图片要达到特定宽度(例如 width: 100%
),系统就会根据宽高比来计算高度。
此 aspect-ratio
值由主流浏览器在处理 HTML 时计算,而不是使用默认的用户代理样式表(请参阅这篇博文深入探究原因),因此该值的显示方式略有不同。例如,Chrome 会在“元素”面板的“样式”部分中按如下方式显示该元素:
img[Attributes Style] {
aspect-ratio: auto 640 / 360;
}
Safari 与此类似,使用 HTML 属性样式源。Firefox 根本不会在 Inspector 面板中显示这个计算出的 aspect-ratio
,而是将其用于布局。
上述代码的 auto
部分非常重要,因为它会导致图片尺寸在图片下载后替换默认宽高比。如果图片尺寸不同,这仍然会导致图片加载后出现一些布局偏移,但这可确保图片宽高比可用时仍然使用,以防 HTML 不正确。即使实际宽高比与默认宽高比不同,与未提供尺寸的图片的 0x0 默认尺寸相比,它仍然会导致布局偏移更小。
如需深入探究宽高比,并进一步考虑自适应图片,请参阅利用媒体宽高比实现网页加载不卡顿。
如果图片位于容器中,可以使用 CSS 根据容器的宽度调整图片大小。我们设置 height: auto;
以避免使用固定的图片高度值。
img {
height: auto;
width: 100%;
}
自适应图片呢?
使用自适应图片时,srcset
会定义您允许浏览器选择的图片以及每张图片的大小。为确保可以设置 <img>
宽度和高度属性,每张图片应使用相同的宽高比。
<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>
Chrome、Firefox 和 Safari 现在支持对给定 <picture>
元素内的 <source>
元素设置 width
和 height
:
<picture>
<source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg" width="480" height="400" />
<source media="(min-width: 800px)" srcset="puppy-800w.jpg" width="800" height="400" />
<img src="puppy-800w.jpg" alt="Puppy with balloons" width="800" height="400" />
</picture>
广告、嵌入内容和其他延迟加载的内容
图片并不是导致布局偏移的唯一内容类型。广告、嵌入内容、iframe 和其他动态注入的内容都会导致内容在其后的位置下移,从而增加您的 CLS。
广告是导致网页布局发生变化的最大因素之一。广告联盟和发布商通常支持动态广告尺寸。广告尺寸可以提高点击率和参与竞价的广告数量,进而提升广告效果。遗憾的是,由于广告会将您正在查看的可见内容挤到页面下方,导致用户体验不佳。
利用嵌入式微件,您可以在自己的页面上添加可移植的 Web 内容,例如来自 YouTube 的视频、来自 Google 地图的地图和社交媒体帖子。但是,这些微件在加载之前通常并不知道其内容的大小。因此,提供嵌入内容的平台并非总是为其 widget 预留空间,从而导致最终加载时布局发生移位。
处理这些任务的技术都相似。主要区别在于您对将插入的内容拥有多大的控制权限。如果是由广告合作伙伴等第三方插入的,您可能无法知道所插入内容的确切尺寸,也无法控制这些嵌入中发生的任何布局偏移。
为延迟加载的内容预留空间
在内容流中放置延迟加载的内容时,可以通过在初始布局中为它们预留空间来避免布局偏移。
一种方法是添加 min-height
CSS 规则以预留空间,或者(例如,对于广告等自适应广告)使用 aspect-ratio
CSS 属性,方法类似于浏览器为具有所提供尺寸的图片自动使用该属性的方式。
您可能需要使用媒体查询,找出不同设备类型上广告或占位符尺寸的细微差异。
对于可能没有固定高度的内容(例如广告),您可能无法预留出完全避免布局偏移所需的确切空间。如果投放的广告尺寸较小,发布商可以设置较大容器的样式来避免布局偏移,或者根据历史数据选择最可能适合相应广告位的尺寸。这种方法的缺点是会增加页面上的空白空间。
您可以将初始尺寸设为要使用的最小尺寸,对于较大内容,可接受一定程度的偏移。如前所述,使用 min-height
可让父元素根据需要进行扩展,同时降低布局偏移的影响(与空元素的默认大小为 0 像素相比)。
例如,在未返回任何广告时,尽量通过显示占位符来避免填补预留空间。移除为元素预留的空间与插入内容产生的 CLS 一样多。
将延迟加载的内容放在视口中靠下的位置
与注入到视口较低处的内容相比,在靠近视口顶部进行动态注入的内容通常会导致较大的布局偏移。不过,在视口的任意位置注入内容仍会导致一些偏移。如果您无法为注入的内容预留空间,建议您将其放置在页面靠后的位置,以降低对其 CLS 的影响。
避免在没有用户互动的情况下插入新内容
您很可能已遇到过由于在尝试加载网站时界面在视口顶部或底部弹出而导致的布局偏移。与广告类似,这通常发生在移动页面其余内容的横幅广告和表单中:
如果您需要显示这些类型的界面功能,请提前在视口中为其预留足够的空间(例如,使用占位符界面或骨架界面),以便在加载时不会导致页面中的内容出现意外偏移。或者,您也可以在合适的位置叠加内容,确保该元素不在文档流中。如需关于这些类型组件的更多建议,请参阅 Cookie 通知最佳做法一文。
在某些情况下,动态添加内容是用户体验的重要组成部分。例如,向商品列表加载更多商品或更新实时 Feed 内容时。在这些情况下,有几种方法可以避免意外的布局偏移:
- 在固定大小的容器中将旧内容替换为新内容,或使用轮播界面在过渡后移除旧内容。请务必停用所有链接和控件,直到转换完成为止,以防止新内容传入时发生意外点击或点按。
- 让用户开始加载新内容,以免对转换感到惊讶(例如使用“加载更多”或“刷新”按钮)。建议在用户互动之前预提取内容,以便内容立即显示。请注意,在用户输入内容时发生的布局偏移不会计入 CLS。
- 顺畅地加载屏幕外的内容,并在上面叠加一条通知,告知用户该内容(例如,使用“向上滚动”按钮)。
![不会导致 Twitter 和 Chloé 网站出现意外布局偏移的动态内容加载示例](https://web.developers.google.cn/static/articles/optimize-cls/image/examples-dynamic-content-4d11ddda2fa94.png?authuser=9&hl=lv)
动画
更改 CSS 属性值可能会要求浏览器对这些更改做出反应。某些值(例如 box-shadow
和 box-sizing
)会触发重新布局、绘制和合成。更改 top
和 left
属性也会导致布局偏移,即使要移动的元素位于自己的层上也是如此。避免使用这些属性添加动画效果。
其他 CSS 属性可以在不触发重新布局的情况下更改。其中包括使用 transform
动画来平移、缩放、旋转或倾斜元素。
使用 translate
的合成动画不会影响其他元素,因此不会计入 CLS。非合成动画也不会导致重新布局。如需详细了解哪些 CSS 属性会触发布局偏移,请参阅高性能动画。
网络字体
在下载网页字体之前,通常通过以下两种方式之一处理网页字体的下载和呈现:
- 回退字体被网页字体交换,导致“无样式文本”(FOUT) 闪烁。
- “不可见”文本使用后备字体显示,直到有可用的网络字体且该文本可见(FOIT,即不可见的文本闪烁)。
这两种方法都会导致布局偏移。即使文本不可见,它仍会使用后备字体进行布局,因此当网络字体加载时,文本块和周围内容会按照与可见字体相同的方式移动。
以下工具可以帮助您最大限度地减少文本移位:
font-display: optional
可以避免重新布局,因为网页字体在完成初始布局时可用。- 请务必使用适当的后备字体。例如,使用
font-family: "Google Sans", sans-serif;
可确保在加载"Google Sans"
时使用浏览器的sans-serif
后备字体。如果不仅使用font-family: "Google Sans"
指定后备字体,系统将使用默认字体,这种字体在 Chrome 上称为“Times”(一种 Serif 字体,比默认sans-serif
字体的匹配度更差)。 - 使用新的
size-adjust
、ascent-override
、descent-override
和line-gap-override
API,最大限度地减小回退字体与网页字体之间的大小差异,如改进的字体回退博文中所述。 - Font Loading API 可以缩短获取必要字体所需的时间。
- 使用
<link rel=preload>
尽早加载关键网页字体。预加载的字体更有可能满足首次绘制要求,在这种情况下不会发生布局偏移。
如需了解其他字体方面的最佳实践,请参阅字体最佳实践。
确保网页符合存储缓存 (bfcache) 的条件,从而降低 CLS
如需保持较低的 CLS 得分,一种非常有效的方法是确保您的网页可以储存至往返缓存 (bfcache)。
当您离开网页后,bfcache 在浏览器内存中保留网页很短的一段时间,因此,如果您再次访问这些网页,将会恢复到您上次离开时所在的网页。这意味着,完全加载的网页可以即时访问,而不会发生任何在加载期间可能因之前介绍的原因而出现偏移的情况。
虽然这仍然可能意味着初始网页加载会发生布局偏移,但当用户回访页面时,他们不会重复看到相同的布局偏移。您应始终力求避免在初始加载时产生偏移,但在更难完全解析的情况下,您至少可以通过避免任何 bfcache 导航来减少影响。
向后和向后导航在许多网站上很常见。例如,返回内容页、类别页或搜索结果。
在 Chrome 中推出此功能后,CLS 有了显著的改进。
默认情况下,所有浏览器都会使用 bfcache,但出于各种原因,有些网站不符合 bfcache 的条件。请参阅 bfcache 指南,详细了解如何测试和识别阻碍 bfcache 使用的任何问题,以确保充分利用此功能来提高您网站的总体 CLS 得分。
总结
如本指南前面所述,可以采用多种技术来识别和改善 CLS。Core Web Vitals 中内置了一些限制,因此即使您无法完全消除 CLS,使用其中一些技术应该也可以降低影响。这有望帮助您不超出这些限制,为网站用户打造更好的体验。