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
没法写死,这时候得动态测量高度:
- 先渲染一次列表项,用
ref
拿到真实高度并存入数组(类似“预渲染”)。 - 滚动时,根据缓存的高度计算「起始索引」和「偏移量」。
- 复杂场景建议用成熟库(
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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。