虚拟列表 - vue 简易实现

30 阅读1分钟

个人理解

  • 真实列表,是实实在在的把所有数据渲染出来,生成全部数量的dom节点,可以滚动查看
  • 虚拟列表,不渲染全部的节点,在用户视口内,只生成可展示数量的dom节点(可能20个),根据滑动的距离,来确定当前展示的是哪20条数据
<template>
  <!-- 用户视口 -->
  <div class="viewport" ref="viewport" @scroll="onScroll">
    <div ref="scrollbar"></div>
    <div class="list" ref="list">
      <div v-for="item in showList" :key="item.n" class="item">
        {{ item.n }}
      </div>
    </div>
  </div>
</template>
<script>
const bigList = new Array(10000).fill(null).map((item, i) => ({
  n: i + 1,
}));
export default {
  data() {
    return {
      start: 0,
      end: 20,
      viewCount: 20, //显示多少数据
      rowHeight: 20, //每行内容高度
      list: Object.freeze(bigList),
    };
  },
  computed: {
    showList() {
      return this.list.slice(this.start, this.end);
    },
  },
  methods: {
    onScroll() {
      this.offsetTop = this.$refs.viewport.scrollTop; //向上滑动的距离
      this.start = Math.ceil(this.offsetTop / this.rowHeight); //向上滑动的个数
      this.end = this.start + this.viewCount;

      // 滚动一次移动40px, 划过2个元素,但是移动了4个元素的位置,
      // 把卷上去的像素平移回来
      this.$refs.list.style.transform = `translateY(${this.offsetTop}px)`;
    },
  },
  mounted() {
    this.$refs.viewport.style.height = this.rowHeight * this.viewCount + "px";
    this.$refs.scrollbar.style.height =
      this.rowHeight * this.list.length + "px"; //显示出滚动条
  },
};
</script>
<style scoped>
.viewport {
  height: 400px;
  width: 500px;
  background-color: bisque;
  overflow-y: scroll;
  position: relative;
}
.list {
  position: absolute;
  top: 0;
  left: 0;
}
.item {
  height: 20px;
}
</style>