优化CLS指标方法和NEXT的SSR实践处理

CLS指标

必看:CLS-谷歌文档说明

累积布局偏移(Cumulative Layout Shift,CLS),衡量页面上元素位置发生变化的频率和程度。
具体来说,CLS是页面上所有意外布局移动实例的累计得分,每个实例的得分是影响的视口分数和移动距离的乘积。

  • 可以测量视觉稳定性
  • 为了提供良好的用户体验,页面的 CLS 应保持在 0.1 或更少。
  • 频繁的偏移将导致页面重排和重绘,不利于性能优化。
  • 在视窗以外的CLS变动将不会影响CLS指标变动,可以利用这一点进行CLS指标的优化。

作为访问该网站的用户,我们可能无法确定页面何时完成加载。我们可能会尝试点击一个新闻故事,结果页面布局发生了巨大变化。这样一来,我们就会进入错误的页面,不得不浪费时间返回。根据页面的不同,这种情况可能会发生多次。如果频繁发生这种情况,我们就会失去对该页面继续访问的兴趣,导致用户留存度下降。

CLS指标降低的常见原因和解决方法

根据谷歌文档的说法,CLS 较差的最常见原因为:

1. 无尺寸的图像和视频

  • 提前设定好宽度和高度,(尽量不要只设置宽度然后高度自适应)。宽高可以设置在img标签内联的width和height属性中,也可以设置在css中(虽然css静态文件可能出现下载慢样式资源加载不及时的问题,但是基本无影响。)
  • 如果不确定高度,使用图片比例自动计算高度的方法提前预留高度,可以使用css属性aspect-ratio或者width+padding-bottom设置高度。

2. 无尺寸的广告、嵌入和 iframe

由第三方sdk渲染的广告、iframe、嵌入组件等。

  • 建议提前留出渲染空间,但是因为高度不可控,高度可以设置min-height而不是height,尽量减少位移变动。

3. 动态注入的内容

由接口请求回来的数据填充进DOM节点中导致的CLS变动。

让用户启动新内容的加载,例如使用加载更多或刷新按钮,因此这种转变并不奇怪。我们建议在用户互动之前预提取新内容,使其立即显示。请注意,在用户输入内容时在 500 毫秒内的布局偏移不会计入 CLS。

用户交互(点击、输入)之后的500ms内的CLS变动将不计入CLS内。

  • 数据的请求考虑分先后优先级,优先请求页面中位于上方元素,避免下方的元素先加载出来之后,上方的数据填充导致下方的内容被挤压出去,CLS指标就会升高。
  • 将延迟加载的内容放在视口中靠下的位置。看不到就不会影响到。
  • 使用SSR方式在服务端优先渲染,提前将首屏的信息加载完毕,则CLS指标就会很稳定。
  • 在用户交互之前先把数据准备好,使用loading或者延迟加载的方式,在新数据填充之前先不展示给用户。

4. 导致不可见文本闪烁 (FOIT)/无样式文本闪烁 (FOUT) 的网络字体

  • 在声明字体的@font-face中使用font-display: swap;属性,并且font-family设置多个可替代字体。意为在加载出字体文件之前先显示优先级低的font-family字体,之后再替换。这样做可能会造成一些偏移,但是在可以接受的范围之内。
  • 使用 尽早加载关键网页字体。预加载的字体更有可能为首次绘制做好准备,并且不会导致布局偏移。

5. 动画效果引起CLS变动

  • 使用top,left,width,height等css属性都会影响到页面重绘甚至重排,造成CLS的变动。建议使用transform属性去替代,用translate变动top / left,用scale变动width,height。

Next项目使用SSR渲染方式降低CLS指标实践

SSR渲染是单线程同步执行的,会初始化组件和方法,执行生命周期方法有限。
核心点:记住下面的这些会在服务端调用的生命周期和Hook,把数据放里面处理就行。

Next.js在SSR时会调用的React组件生命周期方法:

  1. constructor:组件被创建时调用。
  2. getInitialProps:Next提供的方法,用于在服务器端获取数据以及作为props传递给组件。
  3. render。
    React中的componentDidMount、componentDidUpdate、componentWillUnmount等生命周期方法,不会在服务器端被执行,因为这些方法通常与浏览器中的DOM操作或异步请求相关联,而服务端渲染无法访问浏览器的DOM API。

Next.js在SSR时会调用的Hook组件方法:

  1. useState、useReducer、useContext:会初始化state,但不会更新。
  2. useEffect、useRef、forwardRef、useImperativeHandle等:不会在服务端执行,因为它通常用于处理副作用,例如操作DOM、数据订阅与取消订阅等,这些都是在客户端进行的。
  3. useMemo和useCallback:会在服务端渲染时执行,只是缓存优化的作用在服务端体现不出来。

Class类或者函数

  1. 类和函数会进行初始化,Class执行constructor或者函数同步方法。也因此Mobx在服务端渲染中的数据处理都会放到constructor中。

实际使用注意事项

1. 是否显示组件的enable判断不能涉及到useRef、useEffect、componentDidMount

我们很多组件为了安全,都会这样写

const [enable,setEnable] = useState(false);

useEffect(()=>{
	setEnable(true)
},[])

if(!enable) return false;
return xxx;

这样写你的组件在服务端毛都渲染不出来,因为服务端渲染的时候,初始化enable=false,然后useEffect不执行,到下面直接会判断enable=false,返回。所以正确写法应该是使用useMemo或者useCallback代替useEffect获取enable。

2. 涉及dom节点、window使用的变量全都要做isServerRender的判断。

避免报错。

3. dynamic组件代码分割在SSR下需注意页面上的位置顺序。

如果前面的组件没有做SSR,后面的组件就不要做,否则后面的组件会先渲染出来,又被之前的组件顶下去,位移量就很大,影响CLS。

4. 避免使用JS做媒体查询的方案。

如果真的有这样的需要,比如根据屏幕宽度动态赋值,要么css处理、取消ssr、以及图片还有一种实现方案:使用picture / source标签替代img,在dom节点中自带媒体查询展示处理。
因为图片节点的信息已经在同构中进行了处理,如果服务端直接返回为空图片的情况,客户端不会重新渲染图片链接,因此SSR下的图片在JS的媒体查询方案只能用picture来做。

5. 服务端渲染的时间渲染有坑,注意自测。

服务端渲染的时间全部取自于服务器的时间,和客户端时间无关。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值