前言
虚构列表是前端长列表优化的一个解决方案,就是将一个须要渲染所以列表项的长列表,改为只渲染可视区域内的列表项,但滚动成果还是要和渲染所有列表项的长列表一样。
应用场景:在挪动端应用下拉加载数据的时候,随着一直从服务端拉取数据,数据列表内容越来越多,导致创立了很多的节点,这个时候 vue 的 diff 就须要比照很屡次,造成性能耗费和内存占用。
长列表渲染大量节点导致的问题:
- 占用大量 GPU 资源,导致卡顿
- 节点越多,就越消耗性能
虚构列表的实现分两种:
- 列表项高度固定
- 列表项高度动静
上面以列表项高度固定为例子
实现过程
在编码之前须要理解的信息:
- 容器高度(可视区高度):viewport
- 列表项高度:itemSize
- 可视区域展现列表个数:viewCount
- 列表长度:phantomHeight(itemSize * 列表个数)
- 第一个元素顶部的间隔:startOffset
- 开始元素的下标:startIndex
- 结尾元素的下标:endIndex
关键点就是确定须要渲染的列表个数,而后依据滚动时动静扭转 startIndex、endIndex、startOffset 值,而后对列表项数据进行过滤切割,获取须要渲染的数据列表。
实现代码如下:
<script setup lang='ts'>
import {PropType, ref, computed} from 'vue'
interface listItem {
index: number,
num: number
}
const props = defineProps({
data: {type: Array as PropType<listItem[]>,
default: () => {}
}
})
const viewport = ref<HTMLElement>()
let startIndex = ref<number>(0)
let itemSize = 100
let viewCount = 10
let phantomHeight = itemSize * props.data.length
let startOffset = computed(() => {return startIndex.value * itemSize})
let endIndex = computed(() => {return startIndex.value + viewCount})
let filterData = computed(() => {return props.data.slice(startIndex.value, endIndex.value)
})
const scrollListBox = () => {startIndex.value = Math.floor((viewport.value?.scrollTop || 0) / itemSize)
}
</script>
<template>
{{startIndex}}
{{endIndex}}
<div class="viewport" @scroll="scrollListBox" ref="viewport">
<div class="v-list" :style="{height: phantomHeight +'px', paddingTop: startOffset +'px'}">
<div class="v-list-item" v-for="item in filterData" :key="item.num">
{{item.num}}
</div>
</div>
</div>
</template>
<style scoped>
.viewport {
width: 500px;
height: 500px;
overflow: scroll;
}
.v-list-item {
height: 100px;
width: 100%;
background: #fff;
color: #000;
line-height: 100px;
border-bottom: 1px solid #ccc;
}
</style>