项目中有很多地方需要用到大数据展示,想到了用虚拟列表去完成。(Intersectionobserver()也能实现效果,之后会用这个API去完成一个虚拟列表) 下面是源码 跟使用方法。 喜欢的帮我点个赞谢谢
[jcode](https://code.juejin.cn/pen/7226649346240938036)
import { computed, nextTick, reactive, Ref, watchEffect } from 'vue'
interface VirtualListProps<T> {
listData: Ref<T[]>
scrollBox: Ref<HTMLElement | null | undefined>
items: Ref<HTMLElement | null | undefined>
}
interface VirtualListState {
dataList: any[]
itemBoxHeight: number
itemNum: number
startIndex: number
}
export function useVirtualList<T>(props: VirtualListProps<T>) {
const { listData, scrollBox, items } = props
const state: VirtualListState = reactive({
dataList: [],
itemBoxHeight: 0,
itemNum: 1,
startIndex: 0
})
// 计算虚拟列表项
const virtualList = computed(() => {
let endIndex = state.startIndex + state.itemNum
if (endIndex >= listData.value.length) endIndex = listData.value.length
return listData.value.slice(state.startIndex, endIndex)
})
// 监听滚动事件
const handleScroll = () => {
const curScrollTop: any = scrollBox.value?.scrollTop
if (curScrollTop > state.itemBoxHeight) {
const index = ~~(scrollBox.value!.scrollTop / state.itemBoxHeight)
items.value?.style.setProperty('padding-top', `${index * state.itemBoxHeight}px`)
state.startIndex = index
} else {
items.value?.style.setProperty('padding-top', '0px')
state.startIndex = 0
}
}
watchEffect(() => {
if (listData.value.length > 0) {
nextTick(() => {
// 计算每行高度
state.itemBoxHeight = (items.value?.children[0] as HTMLElement)?.offsetHeight //计算屏幕内能显示的行数 +5是防止下拉过快出现白屏
state.itemNum = ~~(scrollBox.value!.clientHeight / state.itemBoxHeight) + 5 // 设置列表总高度
const listHeight = state.itemBoxHeight * listData.value.length
items.value?.style.setProperty('height', `${listHeight}px`)
})
}
})
return {
state,
virtualList,
handleScroll,
scrollBox,
items
}
}
<script setup lang="ts">
import { useVirtualList } from '@/hooks/event/useVirtualList'
const scrollBox: Ref<HTMLElement | null> = ref(null)
const items: Ref<HTMLElement | null | undefined> = ref(null)
const { virtualList, handleScroll } = useVirtualList({
listData,
scrollBox,
items
})
</script>
<div @scroll="handleScroll" ref="scrollBox">
<div ref="items" >
</div>
</div