详解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中的el-tabs动态设置label属性

    vue如何给element-ui中的el-tabs动态设置label属性

    这篇文章主要介绍了vue如何给element-ui中的el-tabs动态设置label属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 在 Vue.js中优雅地使用全局事件的方法

    在 Vue.js中优雅地使用全局事件的方法

    这篇文章主要介绍了在 Vue.js中优雅地使用全局事件的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • vue2项目中全局封装axios问题

    vue2项目中全局封装axios问题

    这篇文章主要介绍了vue2项目中全局封装axios问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Vue自定义询问弹框和输入弹框的示例代码

    Vue自定义询问弹框和输入弹框的示例代码

    这篇文章主要介绍了Vue自定义询问弹框和输入弹框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • Vite内网ip访问2种配置方式

    Vite内网ip访问2种配置方式

    这篇文章主要给大家介绍了关于Vite内网ip访问的2种配置方式,文中通过实例代码介绍的非常详细,对大家学习或者使用Vite具有一定的参考学习价值,需要的朋友可以参考下
    2023-07-07
  • Element-UI 10个技巧小结

    Element-UI 10个技巧小结

    本文主要介绍了Element-UI 10个技巧小结,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • vue实现指定区域自由拖拽、打印功能

    vue实现指定区域自由拖拽、打印功能

    这篇文章主要为大家详细介绍了vue实现指定区域自由拖拽、打印功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • vue深度优先遍历多层数组对象方式(相当于多棵树、三级树)

    vue深度优先遍历多层数组对象方式(相当于多棵树、三级树)

    这篇文章主要介绍了vue深度优先遍历多层数组对象方式(相当于多棵树、三级树),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 前端文件导出设置responseType为blob时遇到的问题及解决

    前端文件导出设置responseType为blob时遇到的问题及解决

    这篇文章主要给大家介绍了关于前端文件导出设置responseType为blob时遇到的问题及解决方法,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • 详解vuex中mutations方法的使用与实现

    详解vuex中mutations方法的使用与实现

    这篇文章主要为大家详细介绍了vuex中mutations方法的使用与实现的相关知识,文中的示例代码简洁易懂,具有一定的学习价值,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-11-11

最新评论