vue3长列表优化-虚拟列表

主要思路:

1.获取单个元素的高度,然后根据所有数据计算总高度,

2.通过滚动事件计算当前元素的索引,来给列表容器动态添加paddingTop

3.如果不设置paddingTop会怎么样:

因为虚拟列表是根据索引使用slice动态截取数据的,当滚动超过第一个元素时,就会触发slice更新数据。数据更新又会触发重新渲染,如果不加paddingTop的话,元素又会从顶部开始排列下来,这是scrollTop又会变为0。(这时慢慢滑的话会出现过了第一个元素又会从第一个元素开始,出现循环的现象。快速滑的则会出现白屏。)

vue3代码

import {
  reactive,
  ref,
  computed,
  onMounted,
  watchEffect,
  nextTick,
} from "vue";

const scrollBox = ref(null);
const items =ref(null);

const state = reactive({
  DataList: [],
  ItemBoxHeight: 0,
  Itemnum: 1,
  startIndex: 0,
});

const getdata = () => {
  let list = [];
  for (let i = 0; i < 100000; i++) {
    list.push({
      src: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Flmg.jj20.com%2Fup%2Fallimg%2F1114%2F042421133312%2F210424133312-1-1200.jpg&refer=http%3A%2F%2Flmg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1669454206&t=bc5099c7f969e3b09cc744604316d025",
      text: `我是${i}号`,
      tid: i,
    });
  }
  state.DataList = list;
};

onMounted(() => {
  getdata();
});

const virtualList = computed(() => {
  let endIndex = state.startIndex + state.Itemnum;
  if (endIndex >= state.DataList.length) endIndex = state.DataList.length;
  return state.DataList.slice(state.startIndex, endIndex);
});

const doscroll = () => {
  const curScrollTop = 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 (state.DataList.length > 0) {
    nextTick(() => {
      // 计算每行高度
      state.ItemBoxHeight = items.value.children[0].offsetHeight;
      //计算屏幕内能显示的行数   +5是防止下拉过快出现白屏
      state.Itemnum = ~~(scrollBox.value.clientHeight / state.ItemBoxHeight)+5;
      // 设置列表总高度
      const ListHeight = state.ItemBoxHeight * state.DataList.length;
      items.value.style.setProperty("height", `${ListHeight}px`);
    });
  }
});

HTML代码

1. box-sizing: border-box(这个是重点)。因为padding的缘故。

<div ref="scrollBox" class="container" @scroll="doscroll">
    <div ref="items" style="box-sizing: border-box">
      <div class="item" v-for="item in virtualList" :key="item.tid">
        <img :src="item.src" alt="" />
        <span>{{ item.text }}</span>
      </div>
    </div>
  </div>

 CSS

.container {
  height: 600px;
  overflow-y: scroll;
}
.container .item {
  height: 100px;
}
.container .item img {
  width: 100px;
  height: 100%;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值