vue渲染十万条数据进行模拟长列表

vue3 版本:

<template>
  <div class="device">
    <div class="vir-scroll">
      <div class="scroll-Y" @scroll="scroll">
        <div class="parentDom">
          <!-- 占位,根据数据条数生成滚动列表 -->
          <div :style="{ height: data.screenHeight + 'px' }"></div>
          <!-- 虚拟滚动列表 -->
          <div
            class="positionRelative"
            :style="{ transform: data.getTransform }"
          >
            <div
              class="scroll-item"
              v-for="(item, index) in data.visibleData"
              :key="index"
            >
              <div class="scroll-info">helloworld{{ item }}</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { computed, reactive } from "vue";

const data = reactive({
  dataList: Array(300000)
    .fill(0)
    .map((v, i) => `item-${i}`),
  start: 0,
  end: 10,
  itemHeight: 143,
  startOffset: 0,

  visibleData: computed(() => {
    return data.dataList.slice(
      data.start,
      Math.min(data.end, data.dataList.length)
    );
  }),
  getTransform: computed(() => {
    return `translate(0,${data.startOffset}px)`;
  }),
  screenHeight: computed(() => {
    return data.dataList.length * data.itemHeight;
  }),
});

const scroll = (e): void => {
  scrollThrottle(e.target.scrollTop);
};

const scrollThrottle = (scrollTop): void => {
  const topCount = Math.floor(scrollTop / data.itemHeight) - 8;
  // 此时的开始索引
  data.start = topCount >= 0 ? topCount : 0;
  // 此时的结束索引
  data.end = data.start + 25;
  // 此时的偏移量
  data.startOffset = data.start * data.itemHeight;
};
</script>
<style scoped>
.vir-scroll {
  position: fixed;
  left: 0;
  width: 100%;
  height: calc(100% - 196px);
  padding: 0 16px;
  z-index: 10;
  border: 1px solid #ccc;
}
.scroll-Y {
  width: 100%;
  height: 100%;
  overflow-y: auto;
}
.parentDom {
  position: relative;
}
.positionRelative {
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 5px;
  padding-bottom: 12.5px;
}
.scroll-item {
  /* width: 100%; */
  border-bottom: 1px solid #ccc;
  height: 143px;
  background-color: #ffffff;
  font-size: 12px;
  padding: 10px 15px 0px 15px;
}
</style>

vue2版本:

<template>
  <div class="device">
    <div class="vir-scroll">
      <div class="scroll-Y" @scroll="scroll">
        <div class="parentDom">
          <!-- 占位,根据数据条数生成滚动列表 -->
          <div :style="{ height: screenHeight + 'px' }"></div>
           <!-- 虚拟滚动列表 -->
          <div class="positionRelative" :style="{ transform: getTransform }">
            <div class="scroll-item" v-for="item in visibleData" :key="item.index">
              <div class="scroll-info">helloworld</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      /** 数据列表 */
      dataList: [],
      /** 单行高度 */
      itemHeight: 143,
      /** 偏移高度 */
      startOffset: 0,
      /** 起始显示数据 */
      start: 0,
      /** 结束显示数据 */
      end: 10,
    }
  },
  computed: {
    /** 根据每条数据的高度获取总列表高度 */
    screenHeight() {
      return this.dataList.length * this.itemHeight
    },
    /** 前面预留 */
    prevCount() {
      return 8
    },
    /** 后面预留 */
    nextCount() {
      return 25
    },
    /** 每次截取虚拟列表的位置 */
    getTransform() {
      return `translate(0,${this.startOffset}px)`
    },
    /** 虚拟数据 */
    visibleData() {
      return this.dataList.slice(this.start, Math.min(this.end, this.dataList.length))
    },
  },
  methods: {
    /** 列表滚动,暂时不节流,因为滚动快触发次数就少,容易导致没有及时更新数组导致白屏 */
    scroll(e) {
      this.scrollThrottle(e.target.scrollTop)
    },
    /** 滚动函数 */
    scrollThrottle(scrollTop) {
      const topCount = Math.floor(scrollTop / this.itemHeight) - this.prevCount
      // 此时的开始索引
      this.start = topCount >= 0 ? topCount : 0
      // 此时的结束索引
      this.end = this.start + this.nextCount
      // 此时的偏移量
      this.startOffset = this.start * this.itemHeight
    },
  },
}
</script>
<style scoped lang="scss">
.device {
  .vir-scroll {
    // 脱离文档流避免回流
    position: fixed;
    left: 0;
    width: 100%;
    height: calc(100% - 196px);
    padding: 0 16px;
    z-index: 10;

    .scroll-Y {
      width: 100%;
      height: 100%;
      overflow-y: auto;

      .parentDom {
        position: relative;

        .positionRelative {
          width: 100%;
          position: absolute;
          left: 0;
          top: 0;
          border-radius: 5px;
          padding-bottom: 12.5px;

          .scroll-item {
            height: 143px;
            background-color: #ffffff;
            font-size: 12px;
            padding: 10px 15px 0px 15px;
          }
        }
      }
    }
  }
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑豆1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值