详解Vue如何手写一个虚拟列表

 更新时间:2024年04月19日 10:22:03   作者:吃肉不吃皮  
虚拟列表是一种优化长列表渲染的技术,它可以在保持流畅性的同时,渲染大量的数据,本文主要介绍了如何使用vue手写一个虚拟列表,感兴趣的可以了解下

前言

何为虚拟列表?

回答这个问题先想一下,对于返回的几千上万条列表数据,作为前端会如何渲染?

分页呗,最开始想到的就是分页。但是,万一接口没做分页呢?好,就算后端做分页了,那么用户在浏览到第几千条、第几万条数据时前端难道要把这第几千条、几万条的DOM都渲染出来吗?显然是很消耗性能的(毕竟如此多的DOM,而且每个DOM内部都有其他样式细节)如何优化?此时就可以用到虚拟列表了

虚拟列表是一种优化长列表渲染的技术,它可以在保持流畅性的同时,渲染大量的数据。

在传统的列表渲染中,如果列表非常长,会导致渲染时间过长(前面所说的会有几千几万个DOM),页面卡顿,用户体验变得非常差。而虚拟列表则是只渲染可见区域内的数据,而非全部渲染,这样就可以大大提高渲染效率,保持页面流畅性

常见场景

商品列表、社交列表...(暂时想到这两个)

手写虚拟列表

进行虚拟列表的实现之前先搞清楚一个问题:

是什么造成了这么多数据在渲染时产生卡顿?是数据数量太多引起的吗?

准确来说,应该是是由于要渲染的DOM太多造成的卡顿。数据本身只是数据,对于拿到的几千上万条数据它本身的大小对于内存来说只能说是冰山一角吧

虚拟列表的原理

数据我还是这么多数据,但是我不一次性渲染这么多数据,我只渲染其中的一小部分(比如十条),这样当用户滚动的时候就重复变化这一小部分的DOM的渲染效果。这样就能从原先几千上万个DOM变成现在的十个,减少了一个量级,减轻了渲染的压力。而这一小部分显示的区域暂称之为视图区域吧

审查元素,大致的效果如图:

贼长的这部分是所有的数据的盒子所占的大小,但是每次只控制小部分数据在上面的盒子进行显示

虚拟列表的实现

怎么实现上面的效果?这里用到了固定定位和绝对定位

<script lang='ts' setup>
  type Item = {
    id: number
    name: string
  }
  const allListData = ref<Item[]>([])	// 存放十万条数据
  const itemHeight = ref(40) // 每一条(项)的高度
  const count = ref(10) // 一屏展示几条数据
  const startIndex = ref(0) // 开始位置的索引
  const endIndex = ref(10) // 结束位置的索引
  const topVal = ref(0) // 父元素滚动位置

  // 计算展示的列表
  const showListData = computed(() => allListData.value.slice(startIndex.value, endIndex.value))

  // 模拟十万条数据
  const getData = async () => {
    for (let i = 0; i < 10000; i++) {
      allListData.value.push({ name: `第${i}条数据`, id: i })
    }
  }

  // 初始化加载
  onMounted(() => {
    getData()
  })

  // 虚拟列表视口区域的组件实例
  const viewport = ref<HTMLDivElement>()

    const handleScroll = () => {
      console.log('滚动了')
      // 非空判断
      if (!viewport.value) return
      // 获取滚动距离(这里通过组件实例获取的,当然也可以通过在该事件的事件参数中拿到)
      const scrollTop = viewport.value.scrollTop
      // 计算起始下标和结束下标,用于 computed 计算
      startIndex.value = Math.floor(scrollTop / itemHeight.value)
      endIndex.value = startIndex.value + count.value
      // 动态更改定位的 top 值,动态展示相应内容
      topVal.value = viewport.value.scrollTop
    }
</script>

<template>
  <!-- 虚拟列表容器 -->
  <div
    class="viewport"
    ref="viewport"
    :style="{ height: 10条数据撑开的高度 }"
    >
    <!-- 占位元素,高度为所有的数据的总高度 -->
    <div
      class="placeholder"
      :style="{ height:itemHeight * count + 'px' }"
      ></div>
    <!-- 视图区,展示10条数据,注意其定位的top值是变化的 -->
    <div class="list" :style="{ top: topVal + 'px' }">
      <!-- 每一条(项)数据 -->
      <div
        v-for="item in showListData"
        :key="item.id"
        class="item"
        :style="{ height: itemHeight + 'px' }"
        >
        {{ item.name }}
      </div>
    </div>
  </div>
</template>

  <style scoped lang="scss">
  .viewport {
  box-sizing: border-box;
  width: 240px;
  border: solid 1px #000000;
  // 开启滚动条
  overflow-y: auto;
  // 开启相对定位
  position: relative;
  .list {
  width: 100%;
  height: auto;
  // 搭配使用绝对定位
  position: absolute;
  top: 0;
  left: 0;
  .item {
  box-sizing: border-box;
  width: 100%;
  height: 40px;
  line-height: 40px;
  text-align: center;
  // 隔行变色
  &:nth-child(even) {
  background: skyblue;
  }
  &:nth-child(odd) {
  background: #fff;
  }
  }
  }
  }
</style>

一开始视图区域的top值为0,刚好在最顶端,监听滚动事件,当滚动时实时改变top值以及该区域内渲染的数据,从而实现了虚拟列表

视图区域的top值为什么要动态监听?试想一下,现在所有数据的总高度为1000px,视图区域为100px,当用户滚动到500px时,如果视图区域的top值不动态绑定,那么视图区域还停留在top=0(也就是最顶端处),那肯定就看不到最新的视图了

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

相关文章

  • vue项目element UI input框扫码枪扫描过快出现数据丢失问题及解决方案

    vue项目element UI input框扫码枪扫描过快出现数据丢失问题及解决方案

    这篇文章主要介绍了vue项目element UI input框扫码枪扫描过快出现数据丢失问题,输入框要掉两个接口,根据第一个验证接口返回的code,弹不同的框,点击弹框确认再掉第二个接口,需要的朋友可以参考下
    2022-12-12
  • vue中defineProperty和Proxy的区别详解

    vue中defineProperty和Proxy的区别详解

    这篇文章主要介绍了vue中defineProperty和Proxy的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Vue 中插槽的使用总结

    Vue 中插槽的使用总结

    这篇文章主要向大家分享的是Vue 中插槽的使用总结,包括内容有默认插槽、具名插槽、作用域插槽等内容,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-04-04
  • vue-element-admin配置小结

    vue-element-admin配置小结

    本文主要介绍了vue-element-admin配置小结,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • vue.js如何在网页中实现一个金属抛光质感的按钮

    vue.js如何在网页中实现一个金属抛光质感的按钮

    这篇文章主要给大家介绍了关于vue.js如何在网页中实现一个金属抛光质感的按钮的相关资料,文中给出了详细的实例代码以及图文将实现的方法介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • vue Nprogress进度条功能实现常见问题

    vue Nprogress进度条功能实现常见问题

    这篇文章主要介绍了vue Nprogress进度条功能实现,NProgress是页面跳转是出现在浏览器顶部的进度条,本文通过实例代码给大家讲解,需要的朋友可以参考下
    2021-07-07
  • 详解Vue 方法与事件处理器

    详解Vue 方法与事件处理器

    本篇文章主要介绍了详解Vue 方法与事件处理器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • vue组件中传值EventBus的使用及注意事项说明

    vue组件中传值EventBus的使用及注意事项说明

    这篇文章主要介绍了vue组件中传值EventBus的使用及注意事项说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • vue+F2生成折线图的方法

    vue+F2生成折线图的方法

    这篇文章主要为大家详细介绍了vue+F2生成折线图的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Vue3如何利用xlsx、xlsx-js-style导出Excel表格使用(适合新手)

    Vue3如何利用xlsx、xlsx-js-style导出Excel表格使用(适合新手)

    在Vue项目中导出Excel表格是常见的功能,特别是在后台管理系统中,为了方便用户将大量数据保存为本地文件,这篇文章主要给大家介绍了关于Vue3如何利用xlsx、xlsx-js-style导出Excel表格使用的相关资料,需要的朋友可以参考下
    2024-06-06

最新评论