固定高度虚拟滚动列表

149 阅读2分钟

固定宽高虚拟滚动列表‌是一种前端性能优化技术,用于在渲染大量列表数据时,只渲染可视区域内的元素,而不是一次性渲染全部数据。这种技术显著减少了DOM元素数量和计算开销,从而提升页面性能和用户体验。

实现原理

虚拟滚动列表的核心思想是“只渲染用户能看到的那部分”。具体来说,虚拟列表将长列表划分为三个区域:上缓冲区、可视区和下缓冲区。当用户滚动时,只有可视区内的元素会被渲染和更新,而上缓冲区和下缓冲区的元素则不会被渲染或更新,从而减少计算和渲染的开销‌

实现步骤

  1. ‌定义固定宽高的容器‌:创建一个固定宽高的容器作为视口容器元素,限制无限虚拟列表的可视区域大小。
  2. List item创建可滚动区域‌:在视口容器内创建一个可滚动区域,该区域的宽度和高度为父元素的100%,纵向超出部分可滚动。
  3. 内容区域渲染‌:在内容区域内渲染部分列表项,这些列表项的高度是固定的。当用户滚动时,动态改变可视区域内的渲染内容,只更新可视区内的元素‌

实现代码

父页面


<template>
  <div class="container" ref="container">
    <VirtualList :items="tableData" :itemHeight="60" :showItemsAmount="8"></VirtualList>
  </div>
</template>

<script>
import VirtualList from "@/components/VirtualList.vue";
export default {
  components:{
    VirtualList
  },
  data() {
    return {
      tableData: []
    }
  },
  created () {
    for (let i = 0; i < 1000000; i++) {
      this.tableData.push({
        id: i,
        content: `这是第${i+1}条数据`
      })
    }

  },
  methods: {

  }
}
</script>

<style scoped>

</style>

虚拟滚动列表组件

<template>
  <div class="container" :style="{height: containerHeight}" ref="container" @scroll="handleScroll">
    <div class="list-container" :style="{top: itemTop}">
      <!-- 列表元素   -->
      <div class="list-item" v-for="item in showItems" :key="item.id" :style="{ height: itemHeight+ 'px' }">
        {{item.content}}
      </div>
      <!--  真实的dom,完成撑开  -->
      <div class="act-container" :style="{height: actContainerHeight}"></div>
    </div>
  </div>
</template>

<script>
export default {
  name: "VirtualList.vue",

  props:{
    //  列表数据
    items: {
      type: Array,
      default: () => []
    },
    // 列表元素高度
    itemHeight: {
      type: Number,
      default: () => 60
    },
    // 列表元素展示个数
    showItemsAmount: {
      type: Number,
      default: () => 5
    }
  },

  data () {
    return {
      startIndex: 0,  // 要展示的数据的起始下标
      endIndex: this.showItemsAmount    // 要展示的数据的结束下标
    }
  },

  computed:{
    // 最终筛选出的要展示的数据
    showItems () {
      return this.items.slice(this.startIndex, this.endIndex);
    },
    // 列表向上滚动时要动态改变 top 值
    itemTop() {
      return this.startIndex * this.itemHeight + 'px';
    },
    // 容器的高度
    containerHeight() {
      return this.itemHeight * this.showItemsAmount + 'px';
    },
    // 撑开容器内容高度的元素的高度
    actContainerHeight() {
      return this.itemHeight * this.items.length + 'px';
    },
  },

  methods:{
    handleScroll () {
      // 获取container容器滚动条到顶部的距离
      const scrollTop = this.$refs.container.scrollTop;
      // 计算卷去的数据条数,用计算的结果作为获取数据的起始和结束下标
      // 起始的下标就是卷去的数据条数,向下取整
      this.startIndex = Math.floor(scrollTop/this.itemHeight);
      // 结束的下标就是起始的下标加上要展示的数据条数
      this.endIndex = this.startIndex + this.showItemsAmount;
    }
  }
}
</script>

<style lang="scss" scoped>
.container {
  background-color: #286f8d;
  position: relative;
  overflow-y: scroll;
  font-size: 20px;
  line-height: 60px;
  text-align: center;
  .list-container {
    position: absolute;
    top: 0;
    width: 100%;
  }
}
</style>