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虚拟列表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue之el-option下拉框绑定问题

    Vue之el-option下拉框绑定问题

    这篇文章主要介绍了Vue之el-option下拉框绑定问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • Vue项目使用CDN优化首屏加载问题

    Vue项目使用CDN优化首屏加载问题

    这篇文章主要介绍了Vue项目使用CDN优化首屏加载问题,本文以vue、vuex、vue-touter为例,给大家介绍处理流程,需要的朋友可以参考下
    2018-04-04
  • 使用mint-ui实现省市区三级联动效果的示例代码

    使用mint-ui实现省市区三级联动效果的示例代码

    下面小编就为大家分享一篇使用mint-ui实现省市区三级联动效果的示例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • vue之浏览器存储方法封装实例

    vue之浏览器存储方法封装实例

    下面小编就为大家分享一篇vue之浏览器存储方法封装实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • vue富文本编辑器组件vue-quill-edit使用教程

    vue富文本编辑器组件vue-quill-edit使用教程

    这篇文章主要为大家详细介绍了vue富文本编辑器组件vue-quill-edit的使用教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • Vue2 SSR渲染根据不同页面修改 meta

    Vue2 SSR渲染根据不同页面修改 meta

    本篇文章主要介绍了Vue2 SSR渲染根据不同页面修改 meta,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Vue中使用Lodop插件实现打印功能的简单方法

    Vue中使用Lodop插件实现打印功能的简单方法

    这篇文章主要给大家介绍了关于Vue中使用Lodop插件实现打印功能的简单方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Vue具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12
  • vue+canvas绘制时间轴的方法

    vue+canvas绘制时间轴的方法

    这篇文章主要为大家详细介绍了vue+canvas绘制时间轴的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • vue实现点击隐藏与显示实例分享

    vue实现点击隐藏与显示实例分享

    在本篇文章中小编给大家分享了vue如何实现点击隐藏与显示的相关内容,有需要的朋友们跟着学习下。
    2019-02-02
  • Vue2中配置Cesium全过程

    Vue2中配置Cesium全过程

    这篇文章主要介绍了Vue2中配置Cesium全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05

最新评论