当数据量较大(此处设定为10w),而且要用列表的形式展现给用户,如果我们不做处理的话,在浏览器中渲染10w dom节点,是极其耗费时间的,那我的Macbook air举例,10w条数据渲染出来到能看到页面,需要13秒多(实际应该是10秒左右),如果是用户的话肯定是不会等一个网页十几秒的
在这里插入图片描述
我们可以用虚拟列表解决这个问题
一步步来
首先看一下效果
在这里插入图片描述

这是data中的数据

data() {
    return {
      list: [], // 贼大的数组
      li: {
        // 列表项信息
        height: 50,
      },
      container: {
        // 容器信息
        height: 500,
      },
      pos: 1, // 第一排显示的元素的下标
      MAX_NUM: 1, // 在容器内最多显示几个列表项
      timer: null, // 定时器
      carriedOut: true, // 能不能执行操作
    };
  },

然后在mounted中创建一个贼大的数组,在调用test方法计算第一次的虚拟列表中有哪些

mounted() {
    // 创建一个贼大的数据数组
    for (let i = 0; i < 100000; i++) {
      this.list.push(i);
    }
    this.test();
  },

test方法

test() {
      // 节流
      if (this.carriedOut) {
        // 容器跟里面的列表项
        const { container, li } = this;
        // 计算可视区域最多能显示多少个li
        this.MAX_NUM = Math.ceil(container.height / li.height);
        // 获取 overflow:scroll 的元素已滚动的高度
        let scrollTop = this.$refs.container.scrollTop;
        // 计算当前处于第一排的元素的下标
        this.pos = Math.round(scrollTop / li.height);
        // 下方节流操作
        this.carriedOut = false;
        this.timer = setTimeout(() => {
          this.carriedOut = true;
          clearTimeout(this.timer);
        }, 50);
      }
    },

然后是computed

computed: {
    // 用于渲染在页面上的数组
    showList() {
      // 根据计算出来的 第一排元素的下标,和最多显示多少个  用slice实现截取数组
      let arr = this.list.slice(this.pos, this.pos + this.MAX_NUM);
      return arr;
    },
  },

这是html,注意监听了div的scroll事件,并且调用的是test方法

<div class="virtual-list">
    <h1>虚拟列表</h1>
    <div class="container" ref="container" :style="`height:${container.height}px`" @scroll="test">
      <ul :style="`height:${li.height*list.length}px;padding-top:${li.height*pos}px`">
        <li :style="`height:${li.height}px`" v-for="item in 100000" :key="item">{{item}}</li>
      </ul>
    </div>
  </div>

完整源代码

<template>
  <div class="virtual-list">
    <h1>虚拟列表</h1>
    <div class="container" ref="container" :style="`height:${container.height}px`" @scroll="test">
      <ul :style="`height:${li.height*list.length}px;padding-top:${li.height*pos}px`">
        <li :style="`height:${li.height}px`" v-for="item of showList" :key="item">{{item}}</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [], // 贼大的数组
      li: {
        // 列表项信息
        height: 50,
      },
      container: {
        // 容器信息
        height: 500,
      },
      pos: 1, // 第一排显示的元素的下标
      MAX_NUM: 1, // 在容器内最多显示几个列表项
      timer: null, // 定时器
      carriedOut: true, // 能不能执行操作
    };
  },
  mounted() {
    // 创建一个贼大的数据数组
    for (let i = 0; i < 1000; i++) {
      this.list.push(i);
    }
    this.test();
  },
  computed: {
    // 用于渲染在页面上的数组
    showList() {
      // 根据计算出来的 第一排元素的下标,和最多显示多少个  用slice实现截取数组
      let arr = this.list.slice(this.pos, this.pos + this.MAX_NUM);
      return arr;
    },
  },
  methods: {
    test() {
      // 节流
      if (this.carriedOut) {
        // 容器跟里面的列表项
        const { container, li } = this;
        // 计算可视区域最多能显示多少个li
        this.MAX_NUM = Math.ceil(container.height / li.height);
        // 获取 overflow:scroll 的元素已滚动的高度
        let scrollTop = this.$refs.container.scrollTop;
        // 计算当前处于第一排的元素的下标
        this.pos = Math.round(scrollTop / li.height);
        // 下方节流操作
        this.carriedOut = false;
        this.timer = setTimeout(() => {
          this.carriedOut = true;
          clearTimeout(this.timer);
        }, 50);
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.virtual-list {
  text-align: center;
  .container {
    overflow: scroll;
    border: 1px solid red;
  }
}
</style>
Logo

前往低代码交流专区

更多推荐