vue实现虚拟列表

本文介绍在Vue中实现虚拟列表的方法,以解决长列表渲染时的性能瓶颈问题。通过计算可见区域内的数据索引,实现列表内容的高效滚动,提高用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:vue实践
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

需求场景

当列表数据过长,会遇到不使用分页方式来加载长列表的需求。

场景还原

如在数据长度大于 1000 条情况,DOM 元素的创建和渲染需要的时间成本很高,完整渲染列表所需要的时间不可接受,同时会存在滚动时卡顿问题。

需求价值

解决该卡顿问题的痛点在于如何降低长列表DOM渲染成本问题。
1、可以通过后端分页,当我们的列表滑到最底下时,再请求另一页的数据
2、虚拟列表,降低长列表DOM渲染成本问题
3、Object.freeze优化长列表,对于data()或vuex中冻结的对象,vue不会做getter和setter的转换

解决方案

虚拟列表的实现

效果链接
在这里插入图片描述

实现虚拟列表思路

就是处理滚动条滚动后的可见区域的变更,其中具体思路如下:
1.计算当前可见区域起始数据的 startIndex
2.计算当前可见区域结束数据的 endIndex
3.计算当前可见区域的数据,并渲染到页面中
4.计算 startIndex 对应的数据在整个列表中的偏移位置 startOffset,并设置到列表上

代码实现

代码链接
我们首先要考虑的是虚拟列表的 HTML、CSS 如何实现:
1、列表元素(.list-view)使用相对定位
2、使用一个不可见元素(.list-view-phantom)撑起这个列表,让列表的滚动条出现
3、列表的可见元素(.list-view-content)使用绝对定位,left、right、top 设置为 0

<!--/**
 * @author: liuk
 * @date: 2023/1/9
 * @describe: vue实现虚拟表单
 * @solution:
    1.确定可视区域的高度
    2.确定内容区域的总高度,用背景div撑起假的滚动条
    3.通过scroll事件确定显示内容,并用 transform实现实际内容区域移动到指定位置
*/-->
<template>
  <div>
    <h2>vue实现虚拟表单</h2>
    <div class="list-view" :style="{height:`${viewHeight}px`}" @scroll="handleScroll">
      <div
          class="list-view-bg"
          :style="{height:bgHeight}"
      ></div>
      <ul ref="conRef" class="list-view-content">
        <li
            class="list-view-item"
            v-for="(item,index) in vData"
            :key="index"
            :style="{height:`${itemHeight}px`}"
        >
          {{ item?.label }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script lang="ts" setup>

import {reactive, ref, toRefs, computed} from 'vue'
import _ from "lodash";
// refs
const conRef = ref<any>();

const bgHeight = computed(() => `${model.data.length * model.itemHeight}px`)
const model = reactive({
  data: <any>[],
  vData: <any>[],
  viewHeight: 400,
  itemHeight: 30
})
const {data, vData, viewHeight, itemHeight} = toRefs(model)

const handleScroll = (val: any) => {
  let scrollTop: number = val.target.scrollTop
  updateVdata(scrollTop)
}

const updateVdata = (scrollTop: number) => {
  // 获取可见区域的可见列表数量
  const vNum = Math.ceil(model.viewHeight / model.itemHeight)
  // 获取可见区域的开始数据索引
  const start = Math.floor(scrollTop / model.itemHeight)
  // 获取可见区域的结束数据索引
  const end = start + vNum;
  // 计算出可见区域的数据,让vue更新
  model.vData = model.data.slice(start, end)
  // 下拉到可视区域
  conRef.value.style.transform = `translate(0, ${start * model.itemHeight}px`
}

// 异步数据
setTimeout(() => {
  model.data = _.fill(new Array(100), {label: 2})
  updateVdata(0)
})

</script>

<style lang="scss" scoped>
.list-view {
  overflow: auto;
  position: relative;
  width: 200px;
  border: 1px solid #aaa;
  height: 300px;

  .list-view-bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: -1;
  }

  .list-view-content {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    list-style: none;
    padding: 0;
    margin: 0;

    .list-view-item {
      padding: 5px;
      color: #666;
      line-height: 30px;
      box-sizing: border-box;
    }
  }
}
</style>

参考

链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值