前言
当我们需要渲染大量数据时,常规的列表渲染方式会面临性能问题,因为浏览器需要一次性渲染所有的数据。而虚拟列表的原理是只渲染当前可见区域内的数据,而非全部渲染,这样可以有效提高列表的渲染性能。
原理
可以通过监听滚动事件来计算当前可见区域的数据范围,并且使用 CSS 的 transform 属性来实现滚动效果。另外,由于只渲染当前可见区域的数据,因此可以提前计算列表项的高度,进而计算出容器的高度,以便在滚动时准确地计算当前可见区域的数据范围。
总的来说,虚拟列表的原理就是在滚动时只渲染当前可见区域的数据,通过计算容器高度和滚动偏移量来确定当前可见区域的数据范围,从而提高列表的渲染性能。
上代码
<template>
<div class="virtual-list-wrapper" :style="{ height: containerHeight + 'px' }">
<div class="virtual-list" :style="{ transform: 'translateY(' + offsetY + 'px)' }">
<div v-for="(item, index) in visibleData" :key="index" class="virtual-list-item">
{{ item }}
</div>
</div>
</div>
</template>
<script>
import { ref, reactive, watch, nextTick } from 'vue';
export default {
props: {
data: {
type: Array,
required: true,
},
itemHeight: {
type: Number,
default: 50,
},
visibleItemCount: {
type: Number,
default: 10,
},
},
setup(props) {
const startIndex = ref(0);
const endIndex = ref(props.visibleItemCount - 1);
const offsetY = ref(0);
const containerHeight = ref(props.visibleItemCount * props.itemHeight);
const visibleData = reactive([]);
const updateVisibleRange = () => {
const startIndexVal = Math.floor(offsetY.value / props.itemHeight);
const endIndexVal = startIndexVal + props.visibleItemCount - 1;
startIndex.value = Math.max(0, startIndexVal);
endIndex.value = Math.min(props.data.length - 1, endIndexVal);
};
watch(offsetY, updateVisibleRange);
const containerRef = ref(null);
nextTick(() => {
containerHeight.value = containerRef.value.clientHeight;
updateVisibleRange();
});
return {
startIndex,
endIndex,
offsetY,
containerHeight,
visibleData,
containerRef,
updateVisibleRange,
};
},
watch: {
data: {
immediate: true,
handler(newValue) {
this.visibleData.splice(0, this.visibleData.length, ...newValue.slice(this.startIndex, this.endIndex + 1));
},
},
visibleData(newValue) {
this.$emit('update:visibleData', newValue);
},
},
};
</script>
<style scoped>
.virtual-list-wrapper {
overflow: hidden;
}
.virtual-list {
position: relative;
transition: transform 0.3s ease;
}
.virtual-list-item {
height: 50px;
}
</style>