前言
虚拟滚动在前端中是一个很常见的解决方案,由于浏览器对于内存的限制,当页面需要展示大量 DOM 节点(如:列表数据超过 10 万)的时候,如果完整渲染整个 DOM 树,当页面数据需要更新重新渲染的时候就会出现滚动卡顿,这个时候就需要虚拟滚动去模拟浏览器原生滚动事件,从而避免这个卡顿情况。
手写难度:⭐️⭐️⭐️
涉及知识点:
- 滚动监听事件 wheel/move
- 事件节流
- 滚动偏移量 offset
- 按需渲染计算方案
虚拟滚动
实现方案
- 步骤 1: 监听虚拟滚动容器的
wheel
或touchmove
事件 - 步骤 2: 创造子容器用于填充父容器,使得父容器可以滚动
- 步骤 3: 提供一个渲染子元素 item函数,返回 dom 节点
- 步骤 4: 计算每个元素的高度,然后计算出总共应该渲染多少个子元素 item
- 步骤 5: 当发生滚动事件的时候,更新子容器的偏移高度,然后触发 步骤 4
抽象方案
定义一个类Scroll
,接收参数为:
el
列表容器 DOM 节点list
列表数据itemRender
子元素渲染函数itemHeight
子元素高度
使用例子为:
1 | const scroll = new Scroll({ |
同时需要支持以下函数:
update
列表数组更新, 触发重新渲染
虚拟滚动列表执行步骤:
- 构造函数初始化,如:
start
end
代表位置 bindEvents
监听滚动事件,触发后续渲染render
init
初始化一个内置容器,用来放置子元素,从而不影响父容器的高度,使得父容器可以滚动render
计算容器滚动高度和元素 item 渲染高度,判断应该渲染哪部分元素 item
简易版源码实现:
1 | class Scroll { |
这样子看起来虚拟滚动是不是十分简单,但是其实有些功能还需要优化,具体如下:
- 节流触发滚动函数,避免每次滚动都进行更新
- 列表缓存,减少列表渲染样式更新
- 提前进行更新渲染,减少因为滚动导致的更新等等
完整代码我放到 github 上,大家感兴趣可以去看看Github Router完整实现
参考资料
- 本文作者: Qborfy
- 本文链接: https://www.qborfy.com/face100/6-visual-scroll.html
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!