个人理解
- 真实列表,是实实在在的把所有数据渲染出来,生成全部数量的dom节点,可以滚动查看
- 虚拟列表,不渲染全部的节点,在用户视口内,只生成可展示数量的dom节点(可能20个),根据滑动的距离,来确定当前展示的是哪20条数据
<template>
<!-- 用户视口 -->
<div class="viewport" ref="viewport" @scroll="onScroll">
<div ref="scrollbar"></div>
<div class="list" ref="list">
<div v-for="item in showList" :key="item.n" class="item">
{{ item.n }}
</div>
</div>
</div>
</template>
<script>
const bigList = new Array(10000).fill(null).map((item, i) => ({
n: i + 1,
}));
export default {
data() {
return {
start: 0,
end: 20,
viewCount: 20, //显示多少数据
rowHeight: 20, //每行内容高度
list: Object.freeze(bigList),
};
},
computed: {
showList() {
return this.list.slice(this.start, this.end);
},
},
methods: {
onScroll() {
this.offsetTop = this.$refs.viewport.scrollTop; //向上滑动的距离
this.start = Math.ceil(this.offsetTop / this.rowHeight); //向上滑动的个数
this.end = this.start + this.viewCount;
// 滚动一次移动40px, 划过2个元素,但是移动了4个元素的位置,
// 把卷上去的像素平移回来
this.$refs.list.style.transform = `translateY(${this.offsetTop}px)`;
},
},
mounted() {
this.$refs.viewport.style.height = this.rowHeight * this.viewCount + "px";
this.$refs.scrollbar.style.height =
this.rowHeight * this.list.length + "px"; //显示出滚动条
},
};
</script>
<style scoped>
.viewport {
height: 400px;
width: 500px;
background-color: bisque;
overflow-y: scroll;
position: relative;
}
.list {
position: absolute;
top: 0;
left: 0;
}
.item {
height: 20px;
}
</style>