使用虚拟列表
只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能,虚拟列表其实是按需显示的一种实现。
虚拟列表一般包含三个组成部分:可视区域、列表渲染区域、真实列表区域。列表渲染区大于等于可视区。比如容器区域需要渲染三屏的节点,当前展示区上一页下一页,滚动过程中会更流畅
视图结构
按照图示,我们先构造如下的视图结构
1. viewport:可视区域的容器
2. list-phantom:容器内的占位,高度为真实列表区域的高度,用于形成滚动条
3. list-area:列表项的渲染区域
<div className="viewport"> <div className="list-phantom"></div> <div className="list-area"> <!-- item-1 --> <!-- item-2 --> <!-- item-n --> </div> </div>
基本思路
虚拟列表的核心思路是 处理用户滚动时可视区域数据的显示 和 可视区外数据的隐藏,这里为了方便说明,引入以下相关变量:
1. startIndex:可视区域的开始索引
2. endIndex:可视区域的结束索引
3. startOffset:可视区第一个元素的向上偏移量
当用户滚动列表时:
1. 计算可视区域的 开始索引 和 结束索引
2. 根据 开始索引 和 结束索引 渲染数据
3. 计算 第一个元素 偏移量并设置到列表渲染区
具体计算:
先假定每个列表项的高度固定为100px,则我们可设置和推导出:
1. 列表项高度: itemSize = 100
2. 可视区的列表项数量: viewcount = viewport / itemSize
3. 可视区结束索引 :endIndex = startIndex + viewcount
当用户滚动时,逻辑处理如下:
1. 获取可视区滚动距离 scrollTop
;
2. 根据 滚动距离 scrollTop 和 单个列表项高度 itemSize 计算出 开始索引 startIndex 和 结束索引 endIndex;
// 获取startIndex const getStartIndex = (scrollTop) => { return Math.floor(scrollTop / itemSize); // 这里可以思考下,为什么要用 Math.floor };
3. 根据 开始索引 和 单个列表项高度 计算出 可视区第一个元素的向上偏移量 ;
4. 只显示 开始索引 和 结束索引 之间的列表项;
5. 设置 列表渲染区域 list-area
的偏移量为 startOffset
动态高度
以上是列表项高度固定的情况,实际项目中列表项高度通常可能是有图文视频在内,高度不固定,我们可以在内容渲染完成后,获得其高度
1. 构造一个数组,缓存列表每行高度及距离顶部及底部高度
2. 动态计算鼠标滑动时当前应展示的列表区间
3. 在dom节点插入后再计算当前显示的节点实际高度替换缓存中的高度修改可视化位置距离顶部距离
当有 item 项高度变化后,我们只需要维护这一个数组的数据即可,从而大大减少了处理起来的复杂度。
使用 ResizeObserver
可以监听到指定元素的高度的变化。ResizeObserver
可以监听到指定元素的高度的变化,而且是原生浏览器层面的支持,性能方面也是可靠的。兼容性方面,除了IE,其他也都是支持的。
缺点
虚拟列表也不是十全十美的,它会有一些问题,主要是:
1. 滚动过快出现会白屏
2. 滚动时有大量的计算
白屏优化
方案一:增加缓存区
在虚拟列表的原理中有提到过,列表渲染区是可以大于等于可视区,这里的采取措施就是列表渲染区域要大于可视区。
措施:在可视区外设置缓存区,额外渲染合适的列表项。
优势:在滚动过快时,会先显示缓存区中的元素,减少白屏出现的情况。
不足:缓存区域设置过大,也会导致渲染性能变差,需要结合具体的业务场景设置合适的缓存值。
方案二:部分渲染
在前面虚拟列表的原理中也有提到过,对非可见区域中的数据不渲染或部分渲染的技术,这里所用到的就是不可见列表项的部分渲染。
措施:采用skeleton
加载骨架屏来代替原有的不渲染部分,这样当滚动过快时,白屏也就替换为了加载屏。
优势:用户体验上会有所增强。
不足:会额外渲染skeleton
的dom
元素。不过对比整个列表元素的dom
节点来看,可以忽略不计的。
计算优化
我们可以采用二分查找法来进行优化
原文章:
https://zhuanlan.zhihu.com/p/444778554
https://blog.csdn.net/weixin_48403384/article/details/123325706
标签:渲染,上万条,高度,列表,索引,区域,可视区 From: https://www.cnblogs.com/buluzombie/p/17796334.html