优化大数据量页面卡顿:探索新的虚拟列表实现方法_有做过虚拟列表吗 假设数据的高度不是固定的要怎么处理

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

计算startIndexendIndex

现在,我们可以按照之前的计算方式来确定要展示的startIndex和endIndex。具体情况如下:

  1. 滚动的距离在knownHeight(已知总高度)内
    如果起始索引到结束索引都小于已知总高度,我们可以精确计算出startIndex和endIndex。
    当只有起始索引小于已知总高度时,对于剩余部分,我们可以使用preHeight进行模拟计算。
let i = 0
let res = 0
while (res <= scrollTop.value) {
  res += allHeightList.value[i]
  i++
}
startIndex.value = i - 1
if (knownHeight.value - res >= height) {
  while (res <= scrollTop.value + height) {
    res += allHeightList.value[i]
    i++
  }
  endIndex.value = i
} else {
  endIndex.value = allHeightList.value.length + Math.ceil(height - (knownHeight.value - res) / preHeight.value)
}

  1. 滚动的距离超出knownHeight(已知总高度)

在这种情况下,我们无法准确计算startIndex和endIndex,只能通过preHeight进行模拟计算。

startIndex.value = allHeightList.value.length
needShowLength.value = Math.ceil(height / preHeight.value)
endIndex.value = startIndex.value + needShowLength.value

演示效果

下面让我们简单看一下使用该方法的效果(使用了4万条数据)
请注意,这种方法能够应对子项高度不确定的情况,但在处理超出已知总高度的滚动距离时稍显不足。

1709800338750-ezgif.com-video-to-gif-converter.gif

GuDynamicHeightList源码
<template>
  <div
    ref="guList"
    class="gu-list"
    @scroll="onScroll"
  >
    <ul
      ref="virtualList" 
      class="gu-virtual-list"
      :style="{
        height:ListHeight + 'px'
      }"
    >
      <li
        v-for="(item,idx) in showList"
        :key="item[field.key]"
        class="gu-virtual-list-item"
        :class="[active == item[field.value] ? 'active' :'']"
        :style="{
          top:`${ reduceHeight(startIndex+idx) }px`
        }"
        :idx="idx"
        @click="onClick(item)"
      >
        <slot
          name="content"
          :item="item"
        >
          {{ item[field.label] }}
        </slot>
      </li>
    </ul>
  </div>
</template>
  
<script lang='ts' setup name="GuDynamicHeightList">
import { toRefs, ref, computed, onMounted, onUpdated } from 'vue'
import { Obj } from '../../types/utilsType'
  
export type ReplaceFieldType = {
  children?:string,
  value?:string,
  label?:string,
  key?:string,
  [field:string]:any
} 
interface VirtualProps {
  preHeight?:number,
  list:Obj[],
  replaceField?: ReplaceFieldType
}
const defaultField = {
  children: 'children',
  value: 'value',
  label: 'label',
  key: 'key',
}
const props = withDefaults(defineProps<VirtualProps>(), {
  preHeight: 26,
  list: () => ([]),
  replaceField: () => ({
    children: 'children',
    value: 'value',
    label: 'label',
    key: 'key',
  }),
})
const { list, replaceField: propField, preHeight } = toRefs(props)
// 所有项的真实高度
const allHeightList = ref<number[]>([])
const knownHeight = computed(() => allHeightList.value.reduce((p, c) => {
  p += c
  return p
}, 0))
// 总高度
const ListHeight = computed(() => {
  let idx = allHeightList.value.length
  return knownHeight.value + (list.value.length - idx) \* preHeight.value
})
  
const field = computed(() => ({
  ...defaultField,
  ...propField.value,
}))
  
const active = ref('')
const emits = defineEmits(['onClickItem'])
const onClick = (item:Obj) => {
  active.value = item[field.value.value]
  emits('onClickItem', item)
}
// 展示列表data
const showList = ref<Obj[]>([])
const guList = ref()
// 裁剪数组开始项
const startIndex = ref(0)
// 裁剪数组结束项
const endIndex = ref(0)
// 计算需要展示的元素个数
const needShowLength = ref(0)
const virtualList = ref()
const scrollTop = ref(0)
const onScroll = () => {
  const { height } = guList.value.getBoundingClientRect()
  scrollTop.value = guList.value.scrollTop
  if (scrollTop.value + height > knownHeight.value) {
    startIndex.value = allHeightList.value.length


![img](https://img-blog.csdnimg.cn/img_convert/d72f7cfdaf63429a31701413dfec3ce1.png)
![img](https://img-blog.csdnimg.cn/img_convert/7c04b402077e3e46c5efeca67f43eb82.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值