走近Fusion组件——无限滚动

3,708 阅读4分钟

Fusion Design体系在经历阿里巴巴集团内部三年多的锤炼后终于在上个月对外开放了,作为设计系统的基础骨架——Next组件库也在正式开源啦🎉🎉


Fusion Next(包名@alifd/next)是一个基于 React 实现,面向 PC 端的可配置组件库。为阿里集团2000+个项目服务的同时,沉淀出了不少基础组件,现在已经达到50+。这个系列将通过实际需求场景,来介绍Fusion组件库的各种有意思的功能、分析实现原理,欢迎讨论👏

使用场景

长列表情境下,例如一个有上千条数据的商品类目选择,初始化选项组时间较长,有明显卡顿。

原因分析

在React环境下,卡顿可能的原因有很多种,例如随意setState、未设置shouldComponentUpdate父组件更新引起所有子组件重绘、DOM节点多层级深导致HTML解析过慢等。在当前场景下,原因显然是最后一个,页面DOM节点数过多,导致虚拟DOM的对比、渲染消耗过大,页面卡顿。

解决方案

无论是IOS、Android客户端,还是H5、PC页面,业内对于这种长列表的优化都有一致的呼声——虚拟滚动
既然是DOM节点多导致的问题,那就减少节点数。页面DOM结构设计不合理,需要优化DOM结构,移除不必要的节点,减少DOM操作耗费的资源。

效果

先来一组对比数据,对比的是从开始渲染到渲染结束所耗费的时长。从图中可以看出当数据量越来越大时,虚拟滚动表现突出,耗费的时间保持在80ms以内。


原理

屏幕高度有限,用户每次看到的项可能只有十几个,维护一个数组domArray,用来存放最终展示到页面上的列表项,将待渲染列表项个数始终控制在一定范围之内。在滚动时,根据纵向滚动的位置动态更新这个数组,并配合transform: translateY达到无限滚动的效果。


关键点有两个:
  1. 列表体transform: translateY(offset) 值的变化
  2. 最终渲染到页面上的数组domArray的更新规则

1. 相对位移如何变化

translateY(offset)可以使所在元素相对父元素向下偏移offset。jsfiddle.net/youluna/w6d…
用户的上下滚动会引起可视区数组domArray包含元素的变化,当数组的首个元素从第 from 个切换为 from+1 的时,整个列表看起来是向上瞬移了第from个元素的高度,这时利用translateY增加第from个元素的高度,就能维持 第 from+1 个元素在相对位置不变。


2. 数组何时更新

domArray储存着用户可能会看到的那部分列表项,一般取自超长源数据中的一段[from, from + size] 例如下图中的[36, 53]。
  • from值是如何来的?
为保证domArray元素保持较少个数,需要记录滚动距离scrollTop,在滚动时动态增删数组内元素。保证如下公式成立,根据scrollTop的变化动态更新from的值:
前from个元素高度的加和 < 滚动距离scrollTop - bufferHeight < 前from + 1个元素高度的加和

  • from + size 值需要满足什么要求?
要求[from, from + size] 这些元素渲染到页面上的总高度(domsHeight)刚好大于可视区域高度(visibleHeight)+缓冲区(buffer*2)高度,即:
domsHeight > visibleHeight + bufferHeight*2
一旦高度条件不满足,就重新计算更新from、size的值,从而更新domArray。
domArray的更新与translateY值的改变相配合,营造视觉上连续不断的滚动效果。Fusion已内置VirtualList组件,目前已经可以与Select组件Cascader组件配合使用,Table组件较为复杂但也通过自身改造支持了无限滚动。

Fusion Next组件库还有各种有意思的功能,期待大家发掘,快来体验使用吧~ 👉戳我查看 说到体验使用,就想起了组件仓库 github.com/alibaba-fus…,我将在github继续扮演commiter,欢迎各位大佬前来把玩,issue/PR两开花🐒🐒