Code前端首页关于Code前端联系我们

Vue3 虚拟列表是啥?咋在项目里用起来?

terry 2小时前 阅读数 4 #Vue
文章标签 Vue3;虚拟列表

不少做前端的朋友碰到大数据列表渲染时,页面卡得像“老牛拉破车”,滚动都费劲,Vue3 里的虚拟列表就是解决这痛点的利器,但好多人对它是啥、咋用还犯迷糊,今天咱就唠唠Vue3虚拟列表的门道,从基础概念到实战落地一次讲透~

Vue3 虚拟列表是干啥的?

你可以把虚拟列表想象成“会偷懒的渲染管家”——页面上明明要展示几千上万条数据,但它只给你渲染「当前能看到的那部分」,没出现在屏幕里的内容,要么用空白占位,要么干脆不渲染 DOM。

举个现实例子:刷短视频 APP 时,上下滑有几十条视频,但 APP 不会一次性把所有视频都加载出来,只加载你当前看的和前后几条,虚拟列表逻辑差不多,用最少的 DOM 节点,扛住大数据量的渲染压力。

普通列表渲染呢?假设你要展示 1 万条数据,每条生成一个 DOM 节点,浏览器得处理 1 万个节点的渲染、布局、绘制,手机或低配电脑直接卡到“动不了”,虚拟列表把 DOM 数量压到几十条(比如屏幕能装 20 条,就只渲染 20 条左右),性能差距能上天。

为啥不用普通列表渲染,非得搞虚拟列表?

这得从浏览器的“软肋”说起——DOM 渲染是性能黑洞

浏览器处理 DOM 时,要经历“解析 HTML→构建 DOM 树→样式计算→布局→绘制→合成”等步骤,数据量一旦爆炸(5000 条以上),每个步骤的开销都会指数级增长:初始化时页面白屏半天,滚动时帧率暴跌(肉眼可见的卡顿),甚至直接把浏览器干崩溃。

虚拟列表的核心逻辑是“精准控制渲染范围”:通过计算「可视区域高度」「单条数据高度」,算出当前该渲染哪一段数据,只给这部分数据生成 DOM,比如做个 10 万条记录的后台表格,普通渲染绝对“死翘翘”,虚拟列表却能让滚动丝滑得像德芙。

Vue3 里咋实现虚拟列表?

咱分“固定高度”和“动态高度”两种场景说,先从简单的固定高度场景入手(新手友好)。

步骤 1:确定核心参数

  • 「可视区域高度」:给列表容器设固定高度(500px),用 clientHeight 获取。
  • 「单条数据高度」:假设每条列表项高度固定(50px),写死数值就行。
  • 「可视条数」:用「可视区域高度」÷「单条高度」,向上取整(500÷50 = 10 条)。

步骤 2:数据切片与滚动计算

当用户滚动列表时,要算出“当前该渲染哪段数据”:

  • scrollTop(滚动条距离顶部的距离)÷「单条高度」,得到起始索引scrollTop 是 200px,200÷50 = 4,从第 4 条开始渲染)。
  • 起始索引 + 可视条数 = 结束索引,用数组的 slice(起始, 结束) 拿到要渲染的子集。

步骤 3:DOM 占位与偏移

为了让滚动条长度正常(不然用户看不到滚动条,或者滚动条太短),得用一个“占位容器”撑开总高度(总条数 × 单条高度),让渲染的可视区域“对齐”到正确位置——用 transform: translateY(偏移量),偏移量 = 起始索引 × 单条高度。

代码示例(固定高度)

下面是个简化的 Vue3 组件,能直观理解逻辑:

<template>
  <div class="virtual-list" @scroll="handleScroll">
    <!-- 总占位容器:撑开滚动条高度 -->
    <div :style="{ height: totalHeight + 'px' }" />
    <!-- 可视区域容器:用 transform 偏移到正确位置 -->
    <div :style="{ transform: `translateY(${offsetY}px)` }">
      <div 
        v-for="item in visibleData" 
        :key="item.id" 
        class="list-item"
      >
        {{ item.content }}
      </div>
    </div>
  </div>
</template>
<script setup>
import { reactive, computed } from 'vue'
// 模拟 10000 条数据
const data = Array.from({ length: 10000 }).map((_, i) => ({
  id: i,
  content: `第${i+1}条数据`
}))
const itemHeight = 50 // 单条高度(固定)
const listHeight = 500 // 容器高度(固定)
const state = reactive({
  scrollTop: 0,
  visibleCount: Math.ceil(listHeight / itemHeight) // 可视条数
})
// 总高度:所有数据的高度总和
const totalHeight = computed(() => data.length * itemHeight)
// 起始索引:滚动后该从哪条开始渲染
const startIndex = computed(() => Math.floor(state.scrollTop / itemHeight))
// 结束索引:起始 + 可视条数
const endIndex = computed(() => startIndex.value + state.visibleCount)
// 要渲染的子集数据
const visibleData = computed(() => data.slice(startIndex.value, endIndex.value))
// 偏移量:让可视区域对齐到滚动后的位置
const offsetY = computed(() => startIndex.value * itemHeight)
// 监听滚动事件,更新 scrollTop
function handleScroll(e) {
  state.scrollTop = e.target.scrollTop
}
</script>
<style scoped>
.virtual-list {
  height: 500px;
  overflow-y: auto;
  position: relative; /* 给子元素绝对定位用(可选) */
  border: 1px solid #eee;
}
.list-item {
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
}
</style>

动态高度咋处理?

如果列表项高度不固定(比如有的带图片、有的文字多),上面的逻辑会“翻车”——因为 itemHeight 没法写死,这时候得动态测量高度

  1. 先渲染一次列表项,用 ref 拿到真实高度并存入数组(类似“预渲染”)。
  2. 滚动时,根据缓存的高度计算「起始索引」和「偏移量」。
  3. 复杂场景建议用成熟库(vue-virtual-scroller),它内置了高度缓存、动态计算逻辑,不用自己折腾。

用虚拟列表时容易踩哪些坑?

就算理解了原理,实战里稍不注意就掉坑里,这几个“雷区”要避开:

滚动条“失真”

总高度计算错(比如漏乘单条高度)、单条高度测量不准,会导致滚动条长度不对,或者滚动时列表“跳来跳去”,要确保 totalHeight 是「数据总数 × 单条高度」,动态高度场景要精准缓存每个 item 的高度。

分页加载“水土不服”

虚拟列表结合分页时(比如滚动到底部加载下一页),要处理数据增量更新,比如用 data.push(...新数据) 时,得同步更新 totalHeight,不然滚动条会“缩水”。

事件绑定“失效”

因为只有可视区域有真实 DOM,事件(比如点击、hover)只能绑定在渲染的节点上,如果要给所有数据绑定事件,得用“事件委托”(把事件绑在容器上,通过 event.target 判断触发源)。

复杂布局“崩掉”

列表项里有图片、异步组件时,加载完成后高度会变化,导致布局错乱,可以用「占位骨架」+「resize 监听」:图片加载前显示占位,加载完成后更新高度缓存,再触发重新计算。

有没有现成的Vue3虚拟列表库推荐?

不想自己写逻辑?这几个库能帮你“躺平”:

vue-virtual-scroller

功能最全面的选手,支持「固定高度」「动态高度」「虚拟网格」(比如瀑布流),文档详细,社区案例多,适合复杂场景,比如带搜索、筛选的大数据列表。

vue3-virtual-list

轻量级选手,API 简单到“傻瓜式调用”,几行代码就能集成,适合需求简单的项目,比如后台管理系统的表格列表。

Element Plus 的 VirtualList

如果项目用了 Element Plus UI 库,直接用它内置的虚拟列表组件,风格和 UI 库统一,不用额外适配样式,省心。

实战中怎么选:自己写还是用库?

分场景决策更聪明:

  • 简单场景(固定高度+无复杂交互):自己写!既能理解原理,又能控制代码体积(少引入第三方库),比如做个“待办事项列表”,数据量不大但想练手,自己撸逻辑更爽。

  • 复杂场景(动态高度+分页+复杂动画):果断用库!比如电商商品列表(图片高度不一、滚动加载、hover 特效),成熟库已经处理了“高度缓存”“滚动同步”“性能优化”等细节,自己写容易掉头发。

  • 团队协作场景:优先用团队技术栈里的库(比如用 Element 就用它的 VirtualList),风格统一,同事接手时不用重新理解自定义逻辑,维护成本低。

绕了一圈,Vue3虚拟列表核心就是“只渲染看得见的,藏起看不见的”,用最少的DOM节点换最丝滑的体验,不管是自己撸代码练手,还是直接搬库上车,关键得明白原理——解决DOM爆炸的性能问题,实际项目里,得先看需求复杂度,再选实现方式,把虚拟列表的优势发挥到实处,用户滚动时再也不会喊“卡到怀疑人生”啦~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门