Vue2-虚拟滚动

本文探讨了Vue2中使用虚拟滚动来优化页面性能的方法,避免因分片加载导致DOM元素过多造成页面卡顿。虚拟滚动仅渲染可视区域,显著减少内存占用和提高用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分片加载

  • 会导致页面dom元素过多,造成页面卡顿
let totalElement = 100000; // 元素
let index = 0; // 索引

const load = (size = 50) => {
  index++;
  let content;
  requestAnimationFrame(() => {
    for (let i = 0; i <= index * size; i++) {
      const element = document.createElement("div");
      element.innerHTML = i + (index - 1) * size;
      document.body.appendChild(element);
    }
    load();
  }, 0);
  if (index >= totalElement) return;
};
load();

虚拟滚动

  • 只渲染当前可视区域
<template>
  <div class="scroll-container" ref="scrollContainer">
    <div class="scroll-viewport" :style="{transform: `translateY(${offset}px)`}">
      <div class="scroll-item" v-for="item in visibleData" :key="item[keyValue] || item.index">
        <slot :item="item"></slot>
      </div>
    </div>
    <div class="scroll-bar" ref="scrollBar" :style="{height: scrollBarHeight}"></div>
  </div>
</template>
<script>
export default {
  name: 'VirtualList',
  props: {
    keyValue: {
      type: String,
      default: ''
    },
    items: {
      type: Array,
      default: () => []
    },
    // 每一项的高度
    size: {
      type: Number,
      require: true,
      default: 0
    },
    // 展示的数量
    remain: {
      type: Number,
      require: true,
      default: 0
    },
    // 预留的dom元素
    reserve: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      start: 0, // 开始位置
      end: this.remain, // 结束位置
      offset: 0 // 偏移量
    };
  },
  computed: {
    preCount() {
      if (!this.reserve) {
        return Math.min(this.start, this.remain);
      }
      return Math.min(this.start, this.reserve.remain);
    },
    nextCount() {
      if (!this.reserve) {
        return Math.min(this.items.length - this.end, this.remain);
      }
      return Math.min(this.items.length - this.end, this.reserve.end);
    },
    formatData() {
      return this.items.map((item, index) => ({...item, index}));
    },
    visibleData() {
      return this.formatData.slice(this.start - this.preCount, this.end + this.nextCount);
    },
    scrollBarHeight() {
      return this.size * this.items.length + 'px';
    }
  },
  mounted() {
    this.init();
    this.$once('hook:beforeDestroy', () =>{
      this.$refs.scrollContainer.removeEventListener('scroll');
    });
  },
  methods: {
    init() {
      this.$refs.scrollContainer.addEventListener('scroll', this.scrollDebounce());
    },
    handleScroll(e) {
      this.scrollDebounce(e);
    },
    scrollDebounce() {
      return this.debounce((e) => {
        console.log('scroll');
        const scrollTop = e.target.scrollTop;
        this.start = Math.floor(scrollTop / this.size);
        this.end = this.start + this.remain;
        this.offset = scrollTop - this.size * this.preCount - scrollTop % this.size;
      });
    },
    debounce(cb, wait = 10) {
      let timer = null;
      return (e) => {
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        timer = setTimeout(() => {
          cb(e);
          clearTimeout(timer);
          timer = null;
        }, wait);
      };
    }
  }
};
</script>
<style scoped lang="less">
.scroll-container{
  overflow-y: auto;
  position: relative;
  height: 100%;
  .scroll-viewport{
    position: absolute;
    top: 0;
    width: 100%;
  }
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值