前两年项目中经常使用到大数据量列表。优化成虚拟列表,性能提升很多
一、实现思路
1、创建滚动容器
2、设定每行高度,通过 css transform 根据滚动高度计算滚动行数做偏移
大概思路就是这样
// virtualList.vue
<template>
<div
class="container"
ref="list"
@scroll="handleScroll"
style="height: 300px; overflow-y: scroll;"
>
<div
v-for="index in dataSource"
:key="index"
:style="{ transform: `translateY(${index * itemHeight}px)` }"
>
{{ items[index] }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, type PropType } from "vue";
// 父组件传参
const props = defineProps({
// 数据源
items: {
type: Array as PropType<any[]>,
required: true,
},
// 设定每行高度
itemHeight: {
type: Number,
default: 30,
},
});
// 获取滚动容器节点
const list = ref<HTMLDivElement | null>(null);
//
const dataSource = ref<number[]>([]);
const startIndex = ref(0);
const endIndex = ref(10);
// 默认展示数据
dataSource.value = props.items.slice(startIndex.value, endIndex.value);
// 计算起止展示数据索引
const computedataSource = () => {
console.log(list.value)
if (!list.value) return;
const scrollTop = list.value.scrollTop;
const visibleCount = Math.ceil(list.value.clientHeight / props.itemHeight);
startIndex.value = Math.floor(scrollTop / props.itemHeight);
endIndex.value = startIndex.value + visibleCount;
dataSource.value = props.items.slice(startIndex.value, endIndex.value);
};
// 每次滚动触发再次计算
const handleScroll = () => {
computedataSource();
};
onMounted(() => {
// 监听滚动事件
window.addEventListener('resize', computedataSource);
if (list.value) {
list.value.addEventListener('scroll', handleScroll);
}
});
onUnmounted(() => {
window.removeEventListener('resize', computedataSource);
if (list.value) {
list.value.removeEventListener('scroll', handleScroll);
}
});
</script>
<style scoped>
.container {
position: relative;
}
</style>
父组件引用
//parent.vue
<template>
<div>
<h1>虚拟列表--简化版</h1>
<virtual-list :items="listItems" :item-height="30"></virtual-list>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
// 导入组件
import VirtualList from '@/components/virtualList.vue';
export default defineComponent({
components: {
VirtualList,
},
setup() {
const listItems = [];
for (let i =0;i<1000;i++) {
listItems.push(i+1)
}
return {
listItems,
};
},
});
</script>
案例仅供参考学习使用。
更完善的可以使用↓↓↓
vue 官网推荐的 大虚拟列表https://cn.vuejs.org/guide/best-practices/performance.html#virtualize-large-lists !!