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

 更新时间:2023年05月23日 11:37:35   作者:为了摸鱼而战  
虚拟列表是一种技术,它只渲染用户当前可见的列表项,而不是渲染整个列表,这篇文章主要来和大家聊聊虚拟列表的原理与实现,希望对大家有所帮助

前言

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

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

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

什么是虚拟列表

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

虚拟列表的原理是什么

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

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

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

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

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

<template>
  <div class="about">
    <div class="scrollView">
      <div class="list">
        <div class="item" v-for="item in list" :key="item">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
    };
  },
  mounted() {
    const total = 100000;
    const list = [];
    for (let i = 0; i < total; i++) {
      list.push(i);
    }
    this.list = list;
  }
};
</script>
<style>
.scrollView {
  width: 200px;
  height: 300px;
  background-color: red;
  overflow-y: scroll;
  position: relative;
}
.item {
  height: 50px;
}
</style>

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

实现虚拟列表

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

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

  mounted() {
    const total = 100000;
    const list = [];
    for (let i = 0; i < total; i++) {
      list.push(i);
    }
    this.data = list;
    this.list = list.slice(0, 6);
  },

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

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

<template>
  <div class="about">
    <div class="scrollView" @scroll="onScroll" ref="list">
      <!-- 虚拟列表 -->
      <div class="virtualScroller" :style="{ height: listHeight + 'px' }"></div> 
      <div class="list">
        <div class="item" v-for="item in list" :key="item">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
      listHeight: 0,
      itemSize: 50,
      containerHeight: 300,
      visibleCount: 6,
      data: [],
      startOffset: 0,
    };
  },
  mounted() {
    ....
    this.listHeight = list.length * this.itemSize;
    .....
  }
};
</script>
<style>
.list {
  position: absolute;
  top: 0;
  left: 0;
}
</style>

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

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

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

我们监听scrollView的 scroll事件

<div class="scrollView" @scroll="onScroll" ref="list">

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

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

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

onScroll() {
       //当前滚动位置
      let scrollTop = this.$refs.list.scrollTop;
      // 列表开始索引
      let startIndex = Math.floor(scrollTop / this.itemSize);
      // 列表结束索引
      let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
      this.list = this.data.slice(startIndex, endIndex);
      // 列表移动距离
      this.startOffset = scrollTop - (scrollTop % this.itemSize);
    }

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

完整代码如下:

<template>
  <div class="about">
    <div class="scrollView" @scroll="onScroll" ref="list">
      <!-- 虚拟列表 -->
      <div class="virtualScroller" :style="{ height: listHeight + 'px' }"></div>
      <div class="list" :style="{ transform: `translateY(${this.startOffset}px)` }">
        <div class="item" v-for="item in list" :key="item">
          {{ item }}
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
      listHeight: 0,
      itemSize: 50,
      containerHeight: 300,
      visibleCount: 6,
      data: [],
      startOffset: 0,
    };
  },
  mounted() {
    const total = 100000;
    const list = [];
    for (let i = 0; i < total; i++) {
      list.push(i);
    }
    this.listHeight = list.length * this.itemSize;
    this.data = list;
    this.list = list.slice(0, 6);
  },
  methods: {
    onScroll() {
        //当前滚动位置
      let scrollTop = this.$refs.list.scrollTop;
      // 列表开始索引
      let startIndex = Math.floor(scrollTop / this.itemSize);
      // 列表结束索引
      let endIndex = Math.ceil((scrollTop + this.containerHeight) / this.itemSize);
      this.list = this.data.slice(startIndex, endIndex);
      // 列表移动距离
      this.startOffset = scrollTop - (scrollTop % this.itemSize);
    }
  }
};
</script>
<style>
.scrollView {
  width: 200px;
  height: 300px;
  background-color: red;
  overflow-y: scroll;
  position: relative;
}
.item {
  height: 50px;
}
.list {
  position: absolute;
  top: 0;
  left: 0;
}
</style>

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

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

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

参考

「前端进阶」高性能渲染十万条数据(虚拟列表)

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

相关文章

  • TypeScript踩坑之TS7053的问题及解决

    TypeScript踩坑之TS7053的问题及解决

    这篇文章主要介绍了TypeScript踩坑之TS7053的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • 关于vant的时间选择器使用方式

    关于vant的时间选择器使用方式

    这篇文章主要介绍了关于vant的时间选择器使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • Vue Router初始化路由信息详解

    Vue Router初始化路由信息详解

    这篇文章主要为大家详细介绍了Vue Router初始化路由信息的相关知识,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解一下
    2023-11-11
  • vue读取本地的excel文件并显示在网页上方法示例

    vue读取本地的excel文件并显示在网页上方法示例

    这篇文章主要介绍了vue读取本地的excel文件并显示在网页上方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Vue组件通信的四种方式汇总

    Vue组件通信的四种方式汇总

    这篇文章主要给大家介绍了关于Vue组件通信的四种方式,分别是父子组件通信、非父子组件的eventBus通信、利用localStorage或者sessionStorage以及利用Vuex等方法,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2018-02-02
  • VUE多层路由嵌套实现代码

    VUE多层路由嵌套实现代码

    这篇文章主要为大家详细介绍了VUE多层路由嵌套的实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • Vue实现带进度条的文件拖动上传功能

    Vue实现带进度条的文件拖动上传功能

    这篇文章主要介绍了Vue实现带进度条的文件拖动上传功能,本文通过实例代码给大家介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Vue3 编译流程-源码解析

    Vue3 编译流程-源码解析

    今天将从 Vue 的入口文件开始,看看声明了一个 Vue 的单文件之后是如何被 compile-core 编译核心模块编译成渲染函数的。下面小编讲解并附上代码分析展现在文章里,感兴趣的小伙伴不要错过奥
    2021-09-09
  • vue自定义js图片碎片轮播图切换效果的实现代码

    vue自定义js图片碎片轮播图切换效果的实现代码

    这篇文章主要介绍了vue自定义js图片碎片轮播图切换效果的实现代码,需要的朋友可以参考下
    2019-04-04
  • Vue router的addRoute方法实现控制权限方法详解

    Vue router的addRoute方法实现控制权限方法详解

    这篇文章主要介绍了vue动态路由添加,vue-router的addRoute方法实现权限控制流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09

最新评论