前言
在一些业务场景中,前端会遇到不能分页
的列表,并且当列表数据量比较大
时(比如超过1万条),就会影响到页面的性能。这时就需要我们做优化了,而虚拟列表便是其中的一种方式。
什么是虚拟列表
虚拟列表
其实是按需显示
的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。
假如我们有1万条记录,但是我们的可见区域只有展示6条记录,所以我们首次渲染的时候,只需要渲染这6条就行了。当列表滚动的时候,我们就只需要计算可视区域内需要展示哪6条记录,然后将非可视区域内存在的列表项删除就可以了。
实现思路
我们假定每个列表项的高度时一致的。
- 计算当前可视区域起始数据索引(startIndex)
- 计算当前可视区域结束数据索引(endIndex)
- 计算当前可视区域的数据,并渲染到页面中
- 计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset 并设置到列表上
其中 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);