1.定长虚拟列表
-
定义:虚拟列表也有叫无限滚动的,创建一个滚动列表来渲染大量数据
-
使用场景有大量数据需要渲染时。
-
**解决了什么问题?**大量数据需要渲染,例如Vue传统使用V-for遍历会创建大量Node节点,对于内存和渲染时间的占用都是非常大的。
-
怎么做?
- 虽然数据量很大但是我们要展示给用户的并不是全部数据,因此我们只需要根据每个元素高度和视口高度渲染元素即可。
- 然后监听滚动,当滚动时动态切换数据
- 将内容盒子随着滚动进行Y轴的偏移
-
HTML和css结构
-
<div class="container" :style="container_style"> <!-- 容器盒子-》监听该容器的滚动事件 --> <div class="scroll_container" ref="scroll_ref"> <!-- 撑起盒子高度 --> <div class="pillar" ></div> <!-- 内容列表用来渲染数据的盒子 --> <div class="items" ref="items_ref" > <div :style="item_style" v-for="item in renderData" key="item" class="item">{{ item }}</div> </div> </div> </div>
-
css使用动态属性去绑定结构很清晰,可扩展性很强 <style lang="scss" scoped> .container{ width: v-bind('props.style.width'); height: v-bind('props.style.height'); background: v-bind('props.style.background'); .scroll_container::-webkit-scrollbar { width: 10px; /* 滚动条的宽度 */ background-color: #f5f5f5; /* 滚动条的背景颜色 */ } .scroll_container::-webkit-scrollbar-thumb { background-color: greenyellow; /* 滚动条拖动块的颜色 */ } .scroll_container{ position: relative; overflow: auto; -webkit-overflow-scrolling:touch; width: 100%; height: 100%; .pillar{ position: absolute; right: 0; left: 0; top: 0; z-index: -1; height: v-bind('scroll_height'); } .items{ height: 100%; z-index: 10; top: 0; left: 0; right: 0; position: absolute; box-sizing: border-box; .item{ width: 100%; line-height: calc(v-bind('props.item_height') * 1px); height: calc(v-bind('props.item_height') * 1px); box-sizing: border-box; } } } } </style>
-
-
Vue核心部分
-
interface IVirtualListPropsStyle { width: string height: string background: string } interface IVirtualListProps { style?: IVirtualListPropsStyle data: any[], show_size?: number item_height?: number, item_style?: Record<string, any> container_style?:Record<string, any> } const props = withDefaults(defineProps<IVirtualListProps>(), { style: () => { return { width: '400px', height: '200px', background:'skyblue' } }, data: ()=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], show_size: 5, item_height:40 }) const scroll_ref = ref<HTMLElement|null>(null) const items_ref = ref<HTMLElement|null>(null) const { scroll_height, renderData } = useVirtualList(props,scroll_ref as Ref<HTMLElement>,items_ref as Ref<HTMLElement>)
-
hooks-》useVirtualLit
import { computed, onMounted, ref, type Ref } from "vue" export default (props: IVirtualListProps, scroll_ref: Ref<HTMLElement> | null, items_ref: Ref<HTMLElement> | null) => { const scrollTop = ref(0) // 定高虚拟列表的当前页展示个数 = 内容容器高度 / 单个内容高度 const show_size = computed(() => Math.ceil(parseInt(props.style.height) / props.item_height)) // 总高度 每个数据的高度×总数据个数 const scroll_height = computed(() => { if (props.data.length === 0) { console.warn(`Not received props.data`) return 0 } return (props.data.length - 1) * props.item_height + 'px' }) // 切片的起始和结束索引 const start_index = ref(0) const end_index = ref(show_size.value) // 切片渲染数据 const renderData = computed(() => props.data.slice(start_index.value, end_index.value)) onMounted(() => { scroll_ref!.value.addEventListener('scroll', (e: Event) => { // show_size.value * props.item_height -》从滚动开始计算 scrollTop.value = (e.target as HTMLElement).scrollTop + show_size.value * props.item_height // 滚动改变索引的偏置 const bias = Math.ceil(scrollTop.value / props.item_height) start_index.value += bias - end_index.value end_index.value = end_index.value + (bias - end_index.value) // 内容容器Y的偏移量 items_ref!.value.style.transform = `translateY(${(e.target as HTMLElement).scrollTop}px)` }) }) return { scroll_height, renderData } }
-
-
结果展示:内容的DOM节点始终只有五个但是渲染的数据很大
体验直接下载 pnpm install fancy_virtual_list 即可,具体功能可看md