Vue中虚拟列表的原理与实现详解

前言

最近在工作中遇到了一个列表的需求,因为做的是C端,所以对性能体验要求很高,主要有下面几点要求,也可以说是痛点

  • 就算加载上千条数据,也能够立即加载,而不会卡顿,等待时间不能太长
  • 加载好以后用户能立马进行交互,而不是要等待,体验必须丝滑

仔细想想, 加载上千条数据,加载时间肯定会相对长一些,而又要求立即加载,这不是一个矛盾的需求么,所以如果使用常规的操作,肯定难以解决这个痛点,所以必须引出我们今天的主角才能解决今天的问题

什么是虚拟列表

虚拟列表是一种技术,它只渲染用户当前可见的列表项,而不是渲染整个列表。

虚拟列表的原理是什么

只渲染可视区域内的列表项,而不是渲染整个列表

当用户滚动容器时,虚拟列表会根据滚动位置和可视区域的大小计算出当前应该显示的列表项。

首先实现一个可以滑动的列表

因为我们主要讲的是虚拟列表,为了不增加大家负担,那么列表容器的高度,列表项的高度,我们都设置为已知的,而实际使用的时候,可以根据场景,动态获取

代码很简单,然后我们一次性渲染10万条数据数据

  1. <template>
  2.      <div class=“about”>
  3.      <div class=“scrollView”>
  4.          <div class=“list”>
  5.          <div class=“item” v-for=“item in list” :key=“item”>
  6.              {{ item }}
  7.          </div>
  8.          </div>;
  9.      </div>
  10.      </div>
  11. </template>
  12. <script>
  13. export default {
  14.      data() {
  15.      return {
  16.          list: [],
  17.      };
  18.      },
  19.      mounted() {
  20.      const total = 100000;
  21.      const list = [];
  22.      for (let i = 0; i < total; i++) {
  23.          list.push(i);
  24.      }
  25.      this.list = list;
  26.      }
  27. };
  28. </script>
  29. <style>
  30. .scrollView {
  31.      width: 200px;
  32.      height: 300px;
  33.      backgroundcolor: red;
  34.      overflowy: scroll;
  35.      position: relative;
  36. }
  37. .item {
  38.      height: 50px;
  39. }
  40. </style>

-1

测试结果非常恐怖,因为数据量庞大,发现才开始的时候一直卡着不动,有1s多的时间是什么也看不到的,这无疑是致命的

实现虚拟列表

列表高度为300,列表项高度为50,那么我们只需要展示6条数据就可以了,因为有可能上拉数据,那么有可能展示第7条数据,所以我们要展示7条数据,但是首次加载我们只用展示6条

所以此时我们改一下mounted生命周期

  1.      mounted() {
  2.      const total = 100000;
  3.      const list = [];
  4.      for (let i = 0; i < total; i++) {
  5.          list.push(i);
  6.      }
  7.      this.data = list;
  8.      this.list = list.slice(0, 6);
  9.      },

问题一:我们只展示可视区域的列表,那么如何显示滚动条呢

这个问题就是一个关键,需要我们设置一个虚拟的列表,定义好高度

  1. <template>
  2.      <div class=“about”>
  3.      <div class=“scrollView” @scroll=“onScroll” ref=“list”>
  4.          <!– 虚拟列表 –>
  5.          <div class=“virtualScroller” :style={ height: listHeight + ‘px’ }></div>
  6.          <div class=“list”>
  7.          <div class=“item” v-for=“item in list” :key=“item”>
  8.              {{ item }}
  9.          </div>
  10.          </div>
  11.      </div>
  12.      </div>
  13. </template>
  14. <script>
  15. export default {
  16.      data() {
  17.      return {
  18.          list: [],
  19.          listHeight: 0,
  20.          itemSize: 50,
  21.          containerHeight: 300,
  22.          visibleCount: 6,
  23.          data: [],
  24.          startOffset: 0,
  25.      };
  26.      },
  27.      mounted() {
  28.      ….
  29.      this.listHeight = list.length * this.itemSize;
  30.      …..
  31.      }
  32. };
  33. </script>
  34. <style>
  35. .list {
  36.      position: absolute;
  37.      top: 0;
  38.      left: 0;
  39. }
  40. </style>

问题二:下拉列表以后,如何让列表内容显示到可视区域内

-2

现在我们的效果是这样的,下拉的时候,列表内容都上移了

所以我们要让列表内容在滚动的时候,移动到可视区域内

我们监听scrollView的 scroll事件

  1. <div class=“scrollView” @scroll=“onScroll” ref=“list”>

onScroll的实现包括以下几个内容

  • 滚动距离:scrollTop
  • 列表开始索引:startIndex
  • 列表结束索引:endIndex
  • 列表移动距离:startOffset: 这个是重点
  1. this.startOffset = scrollTop  (scrollTop % this.itemSize);

所以我们的onScroll方法的实现是

  1. onScroll() {
  2.          //当前滚动位置
  3.          let scrollTop = this.$refs.list.scrollTop;
  4.          // 列表开始索引
  5.          let startIndex = Math.floor(scrollTop / this.itemSize);
  6.          // 列表结束索引
  7.          let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
  8.          this.list = this.data.slice(startIndex, endIndex);
  9.          // 列表移动距离
  10.          this.startOffset = scrollTop  (scrollTop % this.itemSize);
  11.      }

此时我们的虚拟列表就实现了

完整代码如下:

  1. <template>
  2.      <div class=“about”>
  3.      <div class=“scrollView” @scroll=“onScroll” ref=“list”>
  4.          <!– 虚拟列表 –>
  5.          <div class=“virtualScroller” :style={ height: listHeight + ‘px’ }></div>
  6.          <div class=“list” :style={ transform: `translateY(${this.startOffset}px)` }>
  7.          <div class=“item” v-for=“item in list” :key=“item”>
  8.              {{ item }}
  9.          </div>
  10.          </div>
  11.      </div>
  12.      </div>
  13. </template>
  14. <script>
  15. export default {
  16.      data() {
  17.      return {
  18.          list: [],
  19.          listHeight: 0,
  20.          itemSize: 50,
  21.          containerHeight: 300,
  22.          visibleCount: 6,
  23.          data: [],
  24.          startOffset: 0,
  25.      };
  26.      },
  27.      mounted() {
  28.      const total = 100000;
  29.      const list = [];
  30.      for (let i = 0; i < total; i++) {
  31.          list.push(i);
  32.      }
  33.      this.listHeight = list.length * this.itemSize;
  34.      this.data = list;
  35.      this.list = list.slice(0, 6);
  36.      },
  37.      methods: {
  38.      onScroll() {
  39.          //当前滚动位置
  40.          let scrollTop = this.$refs.list.scrollTop;
  41.          // 列表开始索引
  42.          let startIndex = Math.floor(scrollTop / this.itemSize);
  43.          // 列表结束索引
  44.          let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
  45.          this.list = this.data.slice(startIndex, endIndex);
  46.          // 列表移动距离
  47.          this.startOffset = scrollTop  (scrollTop % this.itemSize);
  48.      }
  49.      }
  50. };
  51. </script>
  52. <style>
  53. .scrollView {
  54.      width: 200px;
  55.      height: 300px;
  56.      backgroundcolor: red;
  57.      overflowy: scroll;
  58.      position: relative;
  59. }
  60. .item {
  61.      height: 50px;
  62. }
  63. .list {
  64.      position: absolute;
  65.      top: 0;
  66.      left: 0;
  67. }
  68. </style>

那么虚拟列表和下拉加载更多有什么区别呢

虚拟列表(Virtual List)和下拉加载更多(Infinite Scroll)都是用于优化长列表或大数据集的用户界面的技术,但它们有一些区别。

虚拟列表通过动态渲染当前可见的列表项来提高性能和内存利用率,而下拉加载更多是在用户滚动到列表底部时自动加载更多数据。两者都是为了优化大数据集或长列表的用户界面,提供更好的性能和用户体验。具体选择哪种技术取决于具体的需求和场景。

到此这篇关于vue中虚拟列表的原理与实现详解的文章就介绍到这了,更多相关Vue虚拟列表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

标签

发表评论