Skip to content

虚拟列表

面试题:一次性给你 10000 条数据,前端怎么渲染到页面上?

长列表常见的 3 种处理方式:

  1. 懒加载
  2. 时间分片
  3. 虚拟列表

懒加载

懒加载的原理在于:只有视口内的内容会被加载,其他内容在用户滚动到视口时才会被加载。这可以显著减少初次加载的时间,提高页面响应速度。这样的好处在于:

  1. 节省带宽
  2. 提升用户体验

懒加载的缺点:懒加载只能应对数据不是太多的情况。如果列表项有几万甚至几十万项,最终会有对应数量的 DOM 存在于页面上,严重降低页面性能。

时间分片

时间分片的本质是通过 requestAnimationFrame,由浏览器来决定回调函数的执行时机。大量的数据会被分多次渲染,每次渲染对应一个片段。在每个片段中处理定量的数据后,会将主线程还给浏览器,从而实现快速呈现页面内容给用户。

时间分片的缺点:

  1. 效率低
  2. 不直观
  3. 性能差

总结:无论是懒加载还是时间分片,最终都是将完整数量的列表项渲染出来,这在面对列表项非常非常多的时候,页面性能是比较低的。

虚拟列表

原理:设置一个可视区域,然后用户在滚动列表的时候,本质上是动态修改可视区域里面的内容

例如,一开始渲染前面 5 个项目

image-20240701162418114

之后用户进行滚动,就会动态的修改可视区域里面的内容,如下图所示:

image-20240701162813149

也就是说,始终渲染的只有可视区的那 5 个项目,这样能够极大的保障页面性能。

实现:实现定高的虚拟列表,这里所指的定高是说列表项的每一项高度相同

涉及到的信息:

  1. 可视区域起始数据索引(startIndex)
  2. 可视区域结束数据索引(endIndex)
  3. 可视区域的数据
  4. 整个列表中的偏移位置 startOffset

如下图所示:

image-20240701164454859

接下来整个虚拟列表的设计如下:

html
<div class="infinite-list-container">
  <!-- 该元素高度为总列表的高度,目的是为了形成滚动 -->
  <div class="infinite-list-phantom"></div>
  <!-- 该元素为可视区域,里面就是一个一个列表项 -->
  <div class="infinite-list">
    <!-- item-1 -->
    <!-- item-2 -->
    <!-- ...... -->
    <!-- item-n -->
  </div>
</div>
  • infinite-list-container:可视区域的容器
  • infinite-list-phantom:容器内的占位,高度为总列表高度,用于形成滚动条
  • infinite-list:列表项的渲染区域

如下图所示:

image-20240701165847905

接下来监听 infinite-list-container 的 scroll 事件,获取滚动位置的 scrollTop,因为回头需要设置 list 向下位移的距离

  • 假定可视区域高度固定,称之为 screenHeight
  • 假定列表每项高度固定,称之为 itemSize
  • 假定列表数据称之为 listData(就是很多的列表数据,几万项、几十万项列表数据)
  • 假定当前滚动位置称之为 scrollTop

那么我们能够计算出这么一些信息:

  1. 列表总高度 :listHeight = listData.length * itemSize
  2. 可显示的列表项数 : visibleCount = Math.ceil(screenHeight / itemSize)
  3. 数据的起始索引: startIndex = Math.floor(scrollTop / itemSize)
  4. 数据的结束索引: endIndex = startIndex + visibleCount
  5. 列表显示数据为: visibleData = listData.slice(startIndex, endIndex)

当发生滚动后,由于渲染区域相对于可视区域发生了偏移,因此我们需要计算出这个偏移量,然后使用 transform 重新偏移回可视区域。

偏移量的计算:startOffset = scrollTop - (scrollTop % itemSize)

思考🤔:为什么要减去 scrollTop % itemSize ?

答案:之所以要减去 scrollTop % itemSize,是为了将 scrollTop 调整到一个与 itemSize 对齐的位置,避免显示不完整的列表项。

image-20240701170126764

实战演练:实现定高的虚拟列表

定高的虚拟列表存在一些问题,或者说可以优化的地方:

  1. 动态高度
  2. 白屏现象
  3. 滚动事件触发频率过高

-EOF-

Released under the MIT License.