长列表

是什么?

​ 开发中遇到的数据量较大无法使用分页方式来加载的列表,这种列表称为长列表。

非完整渲染的长列表一般有两种方式:

  • 懒加载:每次只渲染一部分,等剩余部分滚到可见区域,就再渲染一部分。
  • 可视区域渲染:只渲染可见部分,不可见部分不渲染。

虚拟列表的原理

​ 使用数组保存所有列表元素的位置,只渲染可视区内的列表元素,当可视区滚动时,会计算在可视区内应该渲染哪些元素。

参考实现

​ 实现虚拟列表就是处理滚动条滚动后的可见区域的变更。具体实现步骤:

  1. 计算当前可见区域起始数据的startIndex
  2. 计算当前可见区域结束数据的endIndex
  3. 计算当前可见区域的数据,并渲染到页面中
  4. 计算startIndex对应的数据在整个列表中的偏移位置startOffset,并设置到列表上

​ 如下实现中,规定:

  • 列表元素(list-view)使用相对定位
  • 使用不可见元素(list-view-phantom)撑起这个列表,让列表的滚动条出现
  • 列表的可见元素(list-view-content)使用绝对定位,left \ top \ right设置为0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<style>
.list-view{
height: 400px;
overflow: auto;
position: relative;
border: 1px solid #aaa;
}
.list-view-phantom{
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content{
left: 0;
right: 0;
top: 0;
position: absolute;
}
.list-view-item{
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
<body>
<div class="list-view" ref="scrollBox" @scroll="handleScroll">
<div class="list-view-phantom" :style="{height: contentHeight}"></div>
<div ref="content" class="list-view-content">
<div class="list-view-item"
:style="{height: itemHeight+'px'}" v-for="item in visibleData">
{{item}}
</div>
</div>
</div>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contentHeight(){
return this.data.length * this.itemHeight+'px';
}
updateVisibleData(scrollTop=0){
// 当前可视区可以放置的列表条数
const visibleCount = Math.ceil(this.$refs.scrollBox.clientHeight/this.itemHeight);
const start = Math.floor(scrollTop/this.itemHeight);
const end = start + visibleCount;
this.visibleData = this.data.slice(start, end);
this.$refs.content.style.webkitTransform = `translate3d(0, ${start*this.itemHeight}px, 0)`;
}
handleScroll(){
const scrollTop = this.$refs.scrollBox.scrollTop;
this.updateVisibleData(scrollTop);
}