累计布局偏移(CLS)问题处理记录

446 阅读2分钟

问题说明

今天在日常摸鱼(不是)中,发现了performance里有一条醒目的红线

image.png

最终定位到切换路由导航的交互上:

6.gif

这个交互倒没有造成卡顿(火焰图中long task都是vue的update,patch之类处理接口数据的),那这些红色的线表示什么呢?

排查过程

什么是布局偏移?

火焰图中打开一个summary查看:

image.png

get关键词:Cumulative Layout Shifts(累计布局偏移)点击进去查看

官方给出了两个概念:

布局偏移: 每当一个可见元素的位置从一个已渲染帧变更到下一个已渲染帧时,就发生了布局偏移 累计布局偏移: CLS 测量整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移分数

常见的布局偏移例如:

  • 没有预设宽高的图片
  • 网络耗时长的字体
  • 插入广告 都会造成意外的页面偏移,详细处理方案可以看这里

布局偏移分数如何计算?

官方给出的计算方式是布局偏移分数 = 影响分数 * 距离分数

影响分数: 前一帧当前帧的所有不稳定元素的可见区域集合(占总可视区域的部分)就是当前帧的影响分数

距离分数: 布局偏移分数计算公式的另一部分测量不稳定元素相对于可视区域位移的距离。距离分数指的是任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。

以刚才截图的为例:

image.png

影响分数
= 前一帧当前帧的所有不稳定元素*的可见区域集合占可视区域的百分比
= Move from 与 Move to的总面积 / 屏幕可视区域面积
= 94 * 827 / 1920 / 969

距离分数
= 可视区域的最大维度上 不稳定元素所占的比例
= 宽度上不稳地元素所占的比例(PC端宽度大于长度)
= 不稳定元素的宽度 / 可视区域总宽度 = 96 / 1920

所以布局偏移分数 = 影响分数 * 距离分数 = 94 * 827 / 1920 / 969 * 96 / 1920 = 0.0020891920364637085 和最终浏览器给出的分数一致。

image.png

按照官方web性能指标给出的定义,CLS小于0.1应该算是良好。

处理方案

根据官方提供的方案,使用动画代替触发布局偏移的元素

this.topBgStyle = {
    // height: itemHeight * currentIndex + "px" 改进前写法
    height: itemHeight + "px",
    transformOrigin: "top",
    transform: `scaleY(${currentIndex})`
};
this.bottomBgStyle = {
    // height: `calc(100% - ${itemHeight * (currentIndex + 1)}px)`, 改进前写法
    height: "100%",
    transformOrigin: "bottom",
    transform: `scaleY(${1 - itemRatio * (currentIndex + 1)})`
};

再次查看火焰图:

image.png

该问题消失。