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-cli的webpack模板项目配置文件分析

    vue-cli的webpack模板项目配置文件分析

    本篇文章主要对vue-cli的webpack模板项目配置文件进行分析。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • vue实现前端保持筛选条件到url并进行同步参数设计

    vue实现前端保持筛选条件到url并进行同步参数设计

    这篇文章主要为大家介绍了vue实现前端保持筛选条件到url并进行同步参数设计思路详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • uniapp模仿微信实现聊天界面的示例代码

    uniapp模仿微信实现聊天界面的示例代码

    这篇文章主要介绍了如何利用uniapp模仿微信,实现一个聊天界面。文中的示例代码讲解详细,对我们学习Vue有一定的帮助,感兴趣的可以了解一下
    2022-01-01
  • 详细聊聊Vue中的options选项

    详细聊聊Vue中的options选项

    options是new Vue的参数,我们一般称之为选项或者构造选项,下面这篇文章主要给大家介绍了关于Vue中options选项的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • Vuex模块化和命名空间namespaced实例演示

    Vuex模块化和命名空间namespaced实例演示

    这篇文章主要介绍了Vuex模块化和命名空间namespaced的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • IDEA创建Vue项目的两种方式总结

    IDEA创建Vue项目的两种方式总结

    这篇文章主要介绍了IDEA创建Vue项目的两种方式总结,具有很好的参考价值,希望对大家有所帮助。
    2023-04-04
  • vue.js中$watch的用法示例

    vue.js中$watch的用法示例

    这篇文章为大家详细介绍了vue.js中$watch的用法,文中给出了示例代码,相信对大家的理解和学习具有一定的参考借鉴价值,有需要的朋友们可以一起看看吧。
    2016-10-10
  • Nuxt 项目性能优化调研分析

    Nuxt 项目性能优化调研分析

    这篇文章主要介绍了Nuxt 项目性能优化调研分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • vue实现列表无缝循环滚动

    vue实现列表无缝循环滚动

    这篇文章主要为大家详细介绍了vue实现列表无缝循环滚动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • Vue3中jsoneditor的使用示例详解

    Vue3中jsoneditor的使用示例详解

    这篇文章主要为大家详细介绍了Vue3中jsoneditor的使用相关知识,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以跟随小编一起了解下
    2024-01-01

最新评论