如果后端返回一万条数据,你怎么展示?
方法一:打死后端
方法二:手动触底加载更多,或者定时器循环加载
方法三:虚拟列表
所谓虚拟列表,就是不一下子渲染所有的列表,而是只渲染用户看得见的,然后根据用户的滚动,将这个列表移动到可视区域,改变其内容。
这里介绍的是定高的虚拟列表,列表的每一项高度是固定的,下面是刚用vue3写的一个demo:
<template>
<div ref="scroller" id="scroller">
<div id="list" :style="{
transform: `translateY(${translateY}px)`
}">
<Item v-for="item in list" :item="item"></Item>
</div>
<div :style="{
height: holderHeight + 'px'
}"></div>
</div>
</template>
<script setup lang="ts">
import Item from './components/Item.vue';
import { computed, onMounted, reactive, ref, type ComputedRef } from 'vue';
let scroller = ref<HTMLElement | null>(null)
const itemHeight = 133
const len = 1000
const holderHeight = ref(len * itemHeight)
let gap = 4 + Math.floor(document.documentElement.clientHeight / itemHeight)
let scrollTop = ref(0)
const rawList = reactive(Array(len).fill('').map((_, i) => ({
id: i,
price: Math.random() * 1000
})))
let list = computed(_ => rawList.slice(startIndex.value, startIndex.value + gap))
let startIndex: ComputedRef<number> = computed(() => {
if (scrollTop.value < itemHeight * 2) {
return 0
}
return Math.ceil((scrollTop.value - itemHeight * 2) / itemHeight)
})
let translateY: ComputedRef<number> = computed(() => {
return startIndex.value * itemHeight
})
onMounted(() => {
scroller.value?.addEventListener('scroll', e => {
scrollTop.value = (e.target as HTMLElement).scrollTop
})
})
</script>
<style lang="scss">
* {
padding: 0;
margin: 0;
}
#scroller {
height: 100vh;
overflow: auto;
position: relative;
#list {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
will-change: transform;
}
}
</style>
可以看到,一个固定高度的滚动元素,内部是一个用于撑开高度的空元素,以及关键的列表。列表根据高度计算出可以展示多少项,再前后追加两个缓冲。随着用户的滚动,动态计算初始索引及偏移。
定高的虚拟列表比较简单。对于不定高的虚拟列表大家可自行搜索,大致思路应该是给个默认值,比如有一百项,就创建一个长度为100的数组,填充这个默认值,一旦元素真正渲染,就获取高度替换掉对应的默认值,计算索引和偏移都是根据这个数组。