虚拟滚动 - 什么是虚拟滚动?

什么是虚拟滚动?

下面这张图说得很明白: 正常的滚动是指在滚动的容器中渲染出所有的元素, 虚拟滚动是指在滚动的容器只渲染可视区域内的元素,非可视区域内的元素不进行渲染

我们为什么需要虚拟滚动?

因为性能元素,当我们需要的页面需要渲染大量的元素时,虚拟滚动通常能够提升我们页面的性能

一个虚拟滚动的测试场景

测试页面:

测试的方案: 测试页面

startTime定义: 点击按钮这一刻的时间

endTime定义: 弹窗组件最后变化的时间,即 useEffect({})

测试的结果(单位ms):

数据量 正常 table(首次) 正常 table(非首次) 虚拟滚动(首次) 虚拟滚动(非首次)
100 76 63 76 70
500 188 142 64 71
1000 336 218 83 79
2000 586 368 108 102
3000 841 548 142 102
5000 1348 796 205 144
10000 2609 1531 375 234
20000 5212 3127 782 409
30000 7807 4838 1019 523
50000 12972 8238 1909 933
100000 26828 18143 3719 2140

测试结论

  1. 当数据量在 100 时,虚拟滚动的性能和正常 table 的性能是差不多的
  2. 当数据量 > 100 时,虚拟滚动的性能要优于正常 table 的性能
  3. 当数据量到达到 10W 时,尽管虚拟滚动相较于正常 table 性能由很大的提升,但是速度还是非常慢的,打开 dialog 还是需要 3.7s 左右的时间

如何使用虚拟滚动?

虚拟滚动的原理

如果真的想要了解原理,推荐看这两篇文章,讲的很清晰了,这里就不展开说明了

  1. 新手也能看懂的虚拟滚动实现方法
  2. 前端长列表滚动方案探讨,看完不要再说不知道怎么实现虚拟列表了

一些常用的虚拟滚动库

react-window

用于高效呈现大型列表和表格数据的 React 组件 使用场景: 在场景/列表中,如果需要渲染大量的数据,我们可以使用这个库

github react-window

official document

react-virtualized-auto-sizer

一个自动获取父容器宽高的库 使用场景: 这个是一个工具组件,当我们需要监听父容器的宽高变化时,且需要实时获取 width,height 属性时,我们可以使用这个库

github react-virtualized-auto-sizer

react-window-infinite-loader

受 react-virtualized 启发的 InfiniteLoader 组件,可与 react-window 一起使用 使用场景: 一般使用搜索结果页

github react-window-infinite-loader

react-vtree

该软件包为渲染大型树结构提供了轻量级的灵活解决方案。它建立在 react-window 库之上。 使用场景: 需要渲染 tree 数据结构,且该结构数据量很大时,我们可以使用这个库

github react-vtree

antd design 中的虚拟滚动

antd@5 中下面组件中使用到了虚拟滚动

  1. Select
  2. TreeSelect
  3. Table
  4. Tree

思考:我们是否真的需要虚拟滚动?

结论:除非存在明确的需求需要使用虚拟滚动去提升性能,否则不推荐使用虚拟滚动

项目维护角度

从项目迭代维护的角度,一般的原则是:在满足场景需求时,代码和实现方案越简单越好,可读性越高越好,虚拟滚动本身是会在增加代码的复杂性的 以 table 场景举例,直接 antd 能够兼容大部分的业务场景,同时也很容易应对各种需求变化,比如表格的合并,固定行列等等;但是如果使用了虚拟滚动的话,表格合并,固定行列都需要重新去思考怎么封装,开发的成本会增加很多

合理性

从合理性的角度,场景是否真的需要展示这么大的数据量 针对于数据的展示,其实是由通用的降级解决方案的:

  1. 分页
  2. 搜索选择

在使用虚拟滚动之前,应该去思考:常用的降级方案带来的交互性是不是会更好

使用虚拟滚动的注意点

当然,不排除某些场景我们一定要使用虚拟滚动,比如:

  1. 商品展示页的无限下拉加载
  2. 推荐流场景
  3. 通讯录
  4. 大型 tree 结构的展示

在这些通用场景,确实使用虚拟滚动能够带来更多的合理性和更好的交互,但是使用虚拟滚动技术本身是存在限制的, 之前也提及了,虚拟滚动本身的交互拓展性是存在困难的,可能需要自己的二次开发,如果要做特别的交互或者要做特别的场景化定制,可能会存在困难

因此在使用虚拟滚动场景时,一定要沟通好交互方式和展现形式,提前判断开发的难度

QA

Q: 虚拟滚动为什么比传统的渲染性能要好呢?

A:

针对大量的数据,渲染的瓶颈在于我们需要渲染大量的 DOM;这些 DOM 很大部分都是在不可见区域内的,同时渲染这部分 DOM 耗时又非常高; 因此我们可以采取一个方案:通过 js 的计算,只渲染目标区域的 DOM

在这个过程中:js 判断的耗时 < 渲染大量 DOM 的耗时 因此虚拟 DOM 能够提升渲染的性能!

Q: 虚拟 DOM 更新的原理是什么?

A:

  1. 滚动事件发生之后,获取滚动之后的距离 offset
  2. 将 offset 更新到 this 上去,set 操作会自动触发 this.render
  3. 在 render 中我们计算出需要渲染的 DOM 内容,然后将其更新到页面中

参考链接

  1. react-window
  2. react-window-demo
  3. 使用 react-window 虚拟化大型列表
  4. 新手也能看懂的虚拟滚动实现方法
  5. 前端长列表滚动方案探讨,看完不要再说不知道怎么实现虚拟列表了
  6. 文章 demo 仓库
相关推荐
mosen868几秒前
uniapp中uni.scss如何引入页面内或生效
前端·uni-app·scss
白云~️几秒前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
沙尘暴炒饭2 分钟前
uniapp 前端解决精度丢失的问题 (后端返回分布式id)
前端·uni-app
昙鱼17 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
天天进步201522 分钟前
Vue项目重构实践:如何构建可维护的企业级应用
前端·vue.js·重构
小华同学ai25 分钟前
vue-office:Star 4.2k,款支持多种Office文件预览的Vue组件库,一站式Office文件预览方案,真心不错
前端·javascript·vue.js·开源·github·office
APP 肖提莫26 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
问道飞鱼38 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k093339 分钟前
vue中proxy代理配置(测试一)
前端·javascript·vue.js
傻小胖41 分钟前
React 脚手架使用指南
前端·react.js·前端框架