长列表渲染解决方案

  1. Paginatin分页
    1. image.png
  2. 上拉加载,下拉刷新,适合移动端
    1. 上拉加载image.png
    2. 下拉刷新image.png
  3. 分片渲染,增量加载
  4. 虚拟列表


长列表展示场景:

有一个很长的列表需要展示,比如后台一次性返回 2万条数据:

  • 如果是全部直接全部展示,会因为一次性创建了太多的DOM节点,从而导致页面假死,交互严重卡顿;
  • 页面渲染大量DOM,前端渲染也会耗费很长时间,用户等待时间长,白屏时间长。

长列表传统优化点

  1. 采用Pagination分页渲染
  2. 触底请求ajax加载,IntersectionObserver MDN

1 分片渲染

  1. 根据数据大小划分,每次加载固定的数量
  2. 分片渲染缺点:导致页面DOM太多,卡顿

2 虚拟列表

虚拟列表只对可视区域数据进行渲染

  • 只对可视区域内的列表项进行渲染
  • 对非可视区域中的数据不渲染或部分渲染的技术
  • 尽管数据量是上万条,但是HTML标签元素永远就那么几个

虚拟列表核心知识点

  1. 固定高度的虚拟列表
  2. 动态高度的虚拟列表,高度不确定
    1. getBoundingClientRect MDN
    2. 二分查找
  3. scrollTop滚动状态
  4. 滚动到指定的条目

只渲染可视区

把每条数据看作是每一个独立的行数据,用索引来标记每条数据,
用Map对象封装这些块数据,存在浏览器的内存中,
当滚动事件被触发的时候,我们只需要渲染能够映射在viewport中对应的块数据,而不是遍历渲染所有块,能有效的减少HTML DOM的回流和重绘

  • 可视区域(visibleHeight): 根据屏幕可视区域动态计算或自定义固定高度
  • 数据渲染项(visibleCount):可视区域除以行高并向下取整
  • startIndex: 开始索引:初始为0,听过滚动条偏移计算
  • endIndex: 结束索引:startIndex + visibleCount; 数据结束位置索引,可额外预加载几条数据
  1. 每个内容的高度 * 多少条数量 = 总高度
  2. 可视区窗口的高度 / 每条的高度 = 显示多少条
  3. scrollTop - 每条的高度 = startIndex
  4. 显示的列表项数, Number pageSize = Math.ceil(screenHeight / itemSize)
  5. 数据的起始索引, Number startIndex = Math.floor(scrollTop / itemSize)
  6. 数据的结束索引, Number endIndex = startIndex + pageSize
  • itemSize = rowHeight,每一项的高度
  • 列表的总高度 = itemSize * itemCount
    • itemCount = dataSource.length
  • 通过 e.target.scrollTop 获取滚动的高度
  • 显示数据 = dataSource.slice(startIndex, endIndex),或通过 for循环

vlist.jpg

大数据的解决方案

比如有1万条数据,可以看到的部分可能只有几十条,所以,那些我们看不到的dom节点,完全没有必要去渲染
虚拟滚动就是只渲染可视部分的dom节点,在滚动的时候,不断地改变可视区域的值即可

  1. 将wrapper层设置固定高度,并内部可滚动
  2. 内部分三个部分
    1. 上部隐藏区:相当于滚动后隐藏的部分
    2. 实际展示区:实际渲染的内容
    3. 下部隐藏区:未展示的部分
  3. 每次滚动,动态设置,上部隐藏区和下部隐藏区的高度,以及实际展示内容的索引

https://judes.me/frontend/2019/09/17/infinite-table.html
https://zhuanlan.zhihu.com/p/358901891
https://blog.csdn.net/brokenkay/article/details/118566913
渲染的数量是根据 viewport 自动控制的

动态高度

itemSize 列表项的高度是固定的

  • 因为高度固定,所以很轻易的获取列表项的整体高度以及滚动时的显示数据与对应的偏移量
  • 实际应用的时候,当列表中包含文本之类的可变内容,会导致列表项的高度并不相同
    • 常见的场景就是表格的高度被撑开,导致每行 tr的高度超出事前的预设高度
    • 在列表高度不能确定的情况下,无法准确的通过 itemSize 来计算出当前元素所处的y位置

对组件属性 itemSize进行扩展,支持传递类型为 数字、 数组、 函数

  • 一个固定值,如 100,此时列表项是固定高度的
  • 一个包含所有列表项高度的数据,如 [50, 20, 100, 80, …]
  • 一个根据列表项索引返回其高度的函数:(index: number): number
  • 适用于可以预先知道或可以通过计算得知列表项高度的情况,无法解决列表项高度由内容撑开的情况
    • 以预估高度先行渲染,然后获取真实高度并缓存

动态计算 itemSize的高度,解决 react-window的不足

白屏优化

快速滑动就会出现列表闪烁的现象,来不及渲染、空白的现象

  • 解决:渲染用户最大可见条数 + BufferSize
  • 上下多渲染一些元素用来过渡快速滑动时来不及渲染的问题