虚拟列表

冰岩作坊 March 4, 2022

### 需求场景

刷不到底的信息流,无尽图片,复杂的DOM结构…

总之,有长列表,有复杂元素,就可能需要优化

对于很多场景,我们不能一次性加载完所有的数据并且全都渲染出来,这既耗费性能,又影响用户体验。

这里介绍几种常见的解决方法,以及它们的特点。

分页加载

懒加载

虚拟列表

因此在实际情况中是否需要使用,还是需要综合考虑数据量、是否支持请求分页、每个列表项dom元素的复杂程度、是否有筛选项等因素,再去权衡。

原理分析

什么是虚拟列表呢?

虚拟列表是一种根据滚动容器元素的可视区有选择地渲染长列表中某一部分数据的方案

首先,我们要有一个很长很长很长的列表,还要有每个列表项对应的dom素。

我们知道,对于整个列表,如果要将所有元素渲染到屏幕上,数据如果多了,就容易出现卡顿。

因此,我们需要对列表和视窗之间进行一些划分。

简单实现

接下来我们一起实现一个精简的版本,大概思路是:

请求所有数据,对列表分页,监听滚动,计算当前应该展示到第几页,然后截取对应区间的数据,更新展示区域。

具体实现时,现假设我们的列表项是固定长度的,并且能够计算得到。

变量

定义以下变量:

1
        totalList: [], // 总列表        renderList: [], // 渲染列表        curPage: 1// 当前展示的页索引,从1开始        containerTop: 0 // 渲染列表盒子在整个列表盒子的高度        // constants        perPageNum: 10// 每页包含列表项数        itemHeight: 100// 每个列表项的高度        showLen: 3 // 缓冲渲染长度
关于参数的设置
页面结构

来两个盒子:

1
.scroll-box(:style="")    .render-box(:style="")
列表划分

列表划分的关系如下图:

##### 计算

计算页值

计算红色部分视窗对应的页索引curPage

实现:在长列表盒子中监听滚动事件,动态计算curPage的值,注意这里curPage是从1开始计数:

1
handleScroll (e) 

现在我们已经得到了正确的页面值啦,写的时候可以先在浏览器里面上下滑动试试

计算渲染列表及高度

接下来要做的,就是在每次更新curPage时,渲染它及其上下的页面对应的列表项,同时改变渲染部分盒子的高度,使其位于视窗中

计算也很简单,需要考虑以下两种情况:

1. 首次滚动

  1. 持续滚动

补充完善

当然,我们还需要补充一下

最后

以上只是一种比较精简的虚拟列表的实现,不同的方案主要在缓冲设置上的区别比较大,其他的主要是变量设置不同,例如可以维护渲染列表的首尾索引而不是整个渲染列表,不使用多页缓冲进行缓存等

不同版本的实现,可以去读一些较官方版本的源码,如:

react-tiny-virtual-list,react-virtualized, vue virtual scroll list