前言

虚构列表是前端长列表优化的一个解决方案,就是将一个须要渲染所以列表项的长列表,改为只渲染可视区域内的列表项,但滚动成果还是要和渲染所有列表项的长列表一样。

应用场景: 在挪动端应用下拉加载数据的时候,随着一直从服务端拉取数据,数据列表内容越来越多,导致创立了很多的节点,这个时候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 = 100let viewCount = 10let phantomHeight = itemSize * props.data.lengthlet 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>