虚拟列表一种简单的实现方式

909 阅读2分钟

前言

在一些业务场景中,前端会遇到不能分页的列表,并且当列表数据量比较大时(比如超过1万条),就会影响到页面的性能。这时就需要我们做优化了,而虚拟列表便是其中的一种方式。

什么是虚拟列表

虚拟列表其实是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。

假如我们有1万条记录,但是我们的可见区域只有展示6条记录,所以我们首次渲染的时候,只需要渲染这6条就行了。当列表滚动的时候,我们就只需要计算可视区域内需要展示哪6条记录,然后将非可视区域内存在的列表项删除就可以了。

image.png

实现思路

我们假定每个列表项的高度时一致的。

  • 计算当前可视区域起始数据索引(startIndex)
  • 计算当前可视区域结束数据索引(endIndex)
  • 计算当前可视区域的数据,并渲染到页面中
  • 计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset 并设置到列表上

image.png

其中 DOM 结构为:

<div class="list-view" @scroll="handleScroll">
    <div class="list-view-phantom"></div>
    <div class="list-view-content">
      <!-- list-view-item item-1 -->
      <!-- list-view-item item-2 -->
      <!-- ...... -->
      <!-- list-view-item item-n -->
    </div>
</div>

其中:

  • .list-view 为可视区域的一个容器,使用相对定位
  • .list-view-phantom 使用一个不可见元素撑起这个列表,高度为总列表高度,让列表的滚动条出现
  • .list-view-content 列表项的渲染区域,使用绝对定位,left、right、top 设置为 0

接着时逻辑部分,我们监听 list-view 的 scroll 事件,获取滚动位置 scrollTop:

  • 假定可视区域高度固定,称之为 screenHeight
  • 假定列表每项高度固定,称之为 itemSize
  • 假定列表数据称之为 listData
  • 假定当前滚动位置称之为 scrollTop

那么我们可以计算到:

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

当滚动后,由于渲染区域相对于可视区域已经发生了偏移,此时我需要获取一个偏移量 startOffset,通过样式控制将渲染区域偏移至可视区域中。

偏移量 startOffset = scrollTop - (scrollTop % itemSize);

参考: