vue长列表优化
思路: 先指定显示内容区域高度,初始的滚动列表数据高度要比内容区域要高。以向上滚动为例,检测到滚动列表靠近底部时进行一下几个操作:向底部添加x个元素;删除顶部x个元素;向上滚动x个元素高度。向上滚动的目的是抵消头部删除的x个元素产生的滚动位置偏差

直接上代码
<template>
<div class="content">
<div class="list-box" id="listBox" @scroll="scrollI">
<div class="list-body" id="listBody">
<div class="item"v-for="(item, index) in cList" :key="index">{{item}}</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [], // 所有列表数据
cList: [], // 滚动列表
start: 0,
num: 100, // 滚定显示数量
step: 10 // 滚动替换数量
}
},
created() {
for(let i = 0; i < 1000; i++) this.list.push(i);
// 初始显示的内容截取列表num个
this.cList = this.list.slice(this.start, this.start + this.num);
},
mounted() {
this.listBox = document.getElementById("listBox");
this.listBody = document.getElementById("listBody");
this.scrollTimer = null;
// 滚动方向,1向上;2向下
this.dir = 1;
this.scrollTop = 0;
// 可滚动的高度为:内容高度 - 可视区域高度,即滚动条的最大距离
this.scrollHeight = this.listBody.offsetHeight - this.listBox.offsetHeight;
},
methods: {
scrollI(e) {
let top = this.listBox.scrollTop
this.dir = top > this.scrollTop ? 1 : -1;
// 节流
if(!this.scrollTimer) {
// 向上滚动
if(this.dir > 0 && this.scrollHeight - top < 100) {
// 这里可以触发上拉
if(this.start + this.num == this.length) return;
this.scrollTimer = setTimeout(() => {
// 数据是否填充完
let sStep = (this.start + this.step + this.num > this.list.length) ? (this.list.length - this.start - this.num - 1 ) : this.step;
// 移动clist在list中的位置
this.start += this.step;
// 截取指定数量数据,相当于头部删除尾部添加
this.cList = this.list.slice(this.start, this.start + this.num);
// 将滚动位置偏移sStep个元素
this.listBox.scrollTo({
top: top - this.scrollHeight * (sStep / this.num) ,
behavior: 'auto'
})
this.scrollTimer = null;
}, 100);
}else if(this.dir < 0 && top < 100) {
// 向下滚动
// 这里可以触发下拉
if(this.start == 0) return;
this.scrollTimer = setTimeout(() => {
let cStep = this.start - cStep < 0 ? this.start : this.step;
this.start -= cStep;
this.cList = this.list.slice(this.start, this.start + this.num);
this.listBox.scrollTo({
top: top + this.scrollHeight * (cStep / this.num) ,
behavior: 'auto'
})
this.scrollTimer = null;
}, 100);
}
}
}
}
}
</script>
<style scoped>
.content {
/* width: 100vw;
height: 100vh; */
}
.list-box {
position: relative;
width: 400px;
height: 300px;
overflow-x: hidden;
overflow-y: scrill;
}
.list-body {
position: absolute;
top: 0;
min-height: 10px;
height: auto;
margin: 0 auto;
width: 400px;
height: auto;
}
.list-box::-webkit-scrollbar {
display: none;
}
</style>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
上次更新: 2025/09/05, 8:09:00