前言
长列表一般也叫虚拟列表,是一种大数据量下只渲染可见节点避免页面卡顿的优化方案
长列表也有时间分片的做法,比较少用,感兴趣的可以看 高性能渲染十万条数据(时间分片)
前端比较有名的有两个项目:
- react-window
- vue-virtual-scroller
以及 Ant Design 4 的 virtual-list
本文将对这些开源库进行剖析,分析实现原理,并进行各个指标的评估,最终实现一个高可用的长列表组件
主要评估以下几点:
- 渲染:回流, 渲染策略等
- 计算:起止项和偏移位置的计算,总高度的计算
- 功能:自适应高度,其他
- 健壮:是否存在鼠标与滚动条不同步的 bug(计算时总高度增加了,则滚动条会相对鼠标向上)
然后说下看源码的策略,主要看这几点:
- dom 结构
- 查找起始位置
- 计算偏移距离
- 计算总高度
长列表入门
如果还不清楚长列表是什么,可以先看下这篇文章「前端进阶」高性能渲染十万条数据(虚拟列表)
一张图快速入门
下面我们来看看其他开源库都怎么做的
vue-virtual-scroller
功能: 支持自适应高度,横向滚动,图片自适应高度
渲染
dom 结构如下
<!-- position: relative;overflow-y: auto; -->
<div class="vue-recycle-scroller">
<div class="vue-recycle-scroller__item-wrapper" :style="{
'minHeight': totalSize + 'px' }">
<div
v-for="view of pool"
:key="view.nr.id"
:style="{
transform: `translateY(${
view.position}px)` }"
class="vue-recycle-scroller__item-view"
>
<slot
:item="view.item"
:index="view.nr.index"
:active="view.nr.used"
/>
</div>
</div>
</div>
一个相对定位的列表,由 min-height
撑开 wrapper 以产生滚动条
每个列表项进行平移(translateY),这个偏移值为该项在列表中的高度累加值
还有一些 -9999px
的不可见元素,这些其实是缓存池列表项,这个在 节点回收复用 一节会讲到
列表项数据结构:
listItem = {
// 数据项内容
item:Object,
// 非响应式数据
nr:{
id,// 唯一标识
index,// 数据项中的索引
used: Boolean,// 是否已用来显示在可视区域
key,// 数据项中的key
type,// 在对应类型的缓冲池中存取
}
// translateY 值
position: Number,
}
不会产生回流(非自适应高度的情况)
浏览器渲染速度快:进行滚动时