为什么需要虚拟滚动
当某写业务场景的列表存在数据量庞大的情况,这时候如果我们从后端或者某个资源中读取到数据并渲染到前端上的时候,
由于庞大的数据量加上渲染节点的翻倍(包含一些 popover
节点)之类的造成渲染时间比较久,可能引发较长时间的白屏,甚至如果未使用事件委托或者其他优化处理的话
交互行为也会卡顿。
需要考虑的问题
- 数据源
- 数据是否能分页?
- 是,通过接口获取的话则无需考虑占据庞大的存储空间问题
- 否
- 大文件该如何存储?
- 数据是否可通过某种形式压缩后存储localstorage?
- 无法压缩的数据通过文件拆分小文件存储?
- 滚动条
- 自定义滚动条还是动态计算容器高度?
- 用户快速滚动导致渲染速度无法跟上滚动速度的问题
- 检测大范围跳跃
- 异步处理未渲染区域
- 使用 requestAnimationFrame 分批处理
- 临时创建隐藏元素测量高度
- 更新高度缓存和位置信息
- 视觉反馈(过渡效果、添加加载提示)
渲染策略
- 只渲染可见区域的元素:
visibleData = data.slice(visibleStart, visibleEnd)
- 使用绝对定位放置元素:
position: absolute; top: positions[index]
- 容器高度使用总高度
- 预设一个高度*数据量,后续滚动的时候修正每一个元素的实际高度
- 缓冲区处理:当滚动到上下缓冲区+某个阈值的时候进行更新下一个缓冲区元素
关键优化点
性能优化
- 使用
useRef
存储高度和位置信息,避免频繁重渲染 - 使用二分查找快速定位滚动位置
- 使用防抖处理滚动事件