本文是译文,关注Web Vitals Metrics,翻译系列文章中的03篇
原文链接:Cumulative Layout Shift (CLS)
前言
浏览网页时你是否遇到过页面上的内容突然改变的情况?没有任何征兆地文本移动了位置,导致你都不知道读到哪了。或者更有甚者:当你要点击按钮、链接的时候,突然链接位置变了,让你点击到了别的链接上!发生这种情况让人体验非常不好,在另外的场景下会造成灾难性后果。
因为网站中的资源是异步下载的,页面内容也可以动态地添加到已有内容上,所以内容发生非预期变化的场景比较常见。未设置宽高的<image>
、<video>
元素,实际使用的字体与备用大小不一致,可以动态变更大小的第三方广告或者组件都有可能是导致页面发生非预期偏移的“罪魁祸首”。
站点在开发环境的表现与实际用户感受到的有很大差异。开发、生产环境中个性化、第三方内容通常表现也有不一致的情况,比如:测试图片缓存在开发者浏览器缓存之中;本地的API运行得很快,其延迟都无法观测到。CLS通过帮助我们衡量用户实际访问页面时布局偏移发生的概率,来解决开发、生产环境中页面表现不一致的问题。
1.CLS定义
衡量的是页面整个生命周期中每次元素发生的非预期布局偏移得分的总和。每次可视元素在两次渲染帧中的起始位置不同时,就说是发生了LS(Layout Shift)。
1.1CLS得分多少才算良好
CLS<0.1,同样标准需要覆盖站点75%的用户
1.2 深入LS的细节
LS的由Layout Instability API定义,每当视口中两次渲染帧之间的可视元素改变了其起始位置时都会触发layout-shift entries,改变了起始位置的元素被认为是不稳定元素。由于LS只会发生在改变了初始位置的已有元素上,只要新加入的元素并不会造成其他可见元素改变位置,它将不会被当成是LS元素。
(1)LS得分算法
为了计算出LS分数,浏览器关注视口大小和视口中两个渲染帧之间不稳定元素的移动情况。LS得分等于 影响小数与 距离小数的乘积。我们分别来看看这两个因数是如何算出来的。
layout shift score = impact fraction × distance fraction
影响小数
衡量的是不稳定元素渲染帧前后对视口的影响大小
不稳定元素在之前渲染帧中的可视区域 和 当前帧可视区域的并集,即为影响小数。举个例子:在图1中展示的页面渲染帧中,元素占据了一半的视口。之后,在下一个渲染帧,元素往下偏移了视口高度的25%。红色虚线表示的即为两帧不稳定元素可视区域的并集。在这里例子里,占据了视口大小的75%,因此影响小数值为0.75。
距离小数
衡量的是不稳定元素相对于视口移动的距离。
距离小数等于不稳定元素在渲染帧中移动的最大距离(水平或者垂直方向上)除以视口宽或者高(谁大取谁)。在上述例子中,移动距离是垂直方向上,移动了25%视口高,如图1中蓝色箭头,因此距离小数为0.25。因此,在图1给定的例子里面,LS得分为0.75*0.25 = 0.1875。如图1:
接下来的例子演示给已有元素新增内容是如何影响LS得分的。如图2:
Click me按钮挂载在含有黑色文本的灰色盒子里,它将含有白色文本的绿色盒子往下推(部分被推出视口)。图2例子中,灰色盒子虽然改变了大小,但其起始位置没有变化,所以他不是一个不稳定元素。按钮之前不存在,它的起始位置没变,也不是不稳定元素。绿色盒子的起始位置发生了变化,它是一个不稳定元素。另外,由于它部分被推出了视口之外,不可见部分不会参与计算影响小数。绿色盒子在2个渲染帧中出现的可视部分的并集占据了视口的50%。因此影响小数为0.5。图中的紫色箭头说明的是距离小数,绿色盒子大概移动了视口的14%,因此距离小数值为:0.14。合计LS得分为0.5*0.14=0.07。
最后这个例子演示了视口中出现多个不稳定元素的情况。如图3:
第一帧展示的是请求API后4种动物数据,以字母顺序排列。第二帧图加了更多元素。Cat所在的元素起始位置没变,它是稳定的。类似的,新加的元素起始位置也没有变化。标记为Dog、Horse和Zebra的元素都偏移了他们的起始位置,它们都是不稳定元素。红色虚线框代表的是这3个不稳定元素偏移前后占据视口的并集,大概占据了视口区域的38%(影响小数为0.38)。箭头表示着不稳定元素偏离其初始位置的距离。蓝色箭头的Zebra元素,相对视口高度约改变了30%(因此距离小数为:0.3)。 最终Zebra元素的LS得分为:0.38*0.3。
(2)预期LS与非预期LS的对比
用户发起的LS
也不是所有的LS都是个问题,非预期的LS才是。像基于用户操作响应导致的LS是可以接受的,只要偏移发生的时机跟响应用户的操作之间联系足够紧密。举个例子:用户发起的网络请求需要等待一定时间才能完成,最好立即创建布局空间,展示loading正在加载的指示组件来避免请求完成后发生LS。如果用户看不到加载正在进行,或者请求完成之后无从得知,用户在等待期间就会去点击别的地方。发生在用户输入之后500ms以内的LS将会被打上标记,不会参与LS计算。
动画和渐变
动画、渐变如果使用得当的话是一个较好的更新页面内容的办法,不会让用户觉得突兀。页面上内容粗暴的、意外的偏移会带来糟糕的用户体验。内容如果渐进、自然地从一处移动到另外一处有助于用户理解页面上发生的事,也能为用户提供指引。
CSS的transform属性可以做到不引发LS的情况下实现动画效果:
a. 用transform:scale()替代height、width属性的变化;
b. 用translate()替代top、right、bottom、left属性的变化。
2.如何衡量CLS
2.1 现场工具:
Chrome User Experience Report
PageSpeed Insights
Search Console (Core Web Vitals report)
web-vitals JavaScript library
2.2 开发工具:
Chrome DevTools
Lighthouse
WebPageTest
2.3JavaScript代码
(1) API
Layout Instability API + PerformanceObserver
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});
说明:所有具有hadRecentInput标记的layout-shift entries累加值即为CLS值,大部分时候,最终的CLS会在页面unload,但也存在如下例外情况:页面在后台的不参与、缓存中的和iframe中页面也不参与。除了例外情况,由于CLS贯穿整个页面的生命周期的特性也增加了其计算的复杂性。比如用户打开tab页面很久都不关掉、移动端浏览器不会为后台运行的tab执行页面unload回调。同样,细节较多,我们建议使用开发好的工具库来测量CLS。
(2) JavaScript库
import {getCLS} from 'web-vitals';
getCLS(console.log);
3.提升CLS的办法
严格遵循以下原则,就可以避免站点中大部分非预期CLS问题,内容有:
(1)给image
、vide
元素设置size属性,或者预留之后需要的布局空间。这个做法确保让浏览器在图片正在加载时就分配好正确的文档空间。
(2)除非为了响应用户,不要在已有内容上插入内容
(3)把触发布局改变的属性改用transform动画的方式
更多关于优化CLS的方法,请参考:优化CLS章节(待补充)
其它参考:
1.减少layout-shift
2.理解CLS
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。