vue 3虚拟列表处理不等高行的核心是分阶段精准测高、智能缓存与动态映射:用累计高度数组+二分查找定位可视起始索引,结合预估高度启动、首次渲染修正及resizeobserver兜底,并通过transform定位真实dom实现高效渲染。

Vue 3 中处理复杂行高、高度自适应的虚拟列表,核心不是“避免计算”,而是“分阶段精准获取 + 智能缓存 + 动态映射”。固定高度方案靠 index × itemHeight 直接算偏移,而高度不一致时,必须建立“滚动位置 ↔ 累计高度 ↔ 数据索引”的实时映射关系。
用累计高度数组 + 二分查找定位可视起点
每项真实高度不能预知,但可以按顺序测量并缓存成一个前缀和数组(即累计高度数组):
- 假设数据有 1000 条,测得第 0 项高 82px、第 1 项高 145px、第 2 项高 67px……那么累计数组为
[0, 82, 227, 294, ...],其中acc[i]表示前i项总高度 - 滚动时拿到
scrollTop,用二分查找在累计数组中快速定位:第一个acc[i] ≥ scrollTop的i就是可视区域起始索引start - 再从
start向后累加,直到总高度 ≥scrollTop + containerHeight,即可确定end
动态测高要分层:预估启动 + 首次渲染修正 + ResizeObserver 持续兜底
不能等所有 DOM 渲染完再统一测——那样会卡顿且不准。应采用渐进式策略:
-
初始渲染用预估高度(如 120px),保证滚动条存在、首屏可滚动;同时给每个 item 容器打上唯一
data-id或data-index -
item 进入视口时,用
getBoundingClientRect()精确读取一次真实高度,写入缓存 Map,并更新累计数组 -
对内容可能变化的 item(如折叠/展开、图片加载完成),用
ResizeObserver监听其 DOM 节点尺寸变更,触发高度重测与累计数组重算
用虚拟容器撑开滚动空间,样式靠 transform 定位
真实 DOM 只渲染可视区域 ± 缓冲区(如前后各 3 条),其余用占位逻辑撑开滚动高度:
- 外层容器设
overflow: auto,高度固定(如500px) - 内部放一个
<div class="placeholder">,高度设为 <code>acc[totalLength](即全部累计高度),确保滚动条长度准确 - 每个渲染的 item 用
position: absolute+transform: translateY(${acc[index]}px)精准落到对应位置,不依赖文档流 - 高度缓存用
Map<number number></number>或Ref<record number>></record>,key 是数据索引或唯一 ID - 累计数组每次只局部更新(如某项高度变了,只重算它之后的累计值),不用全量重建
- 滚动监听用
throttle或passive: true,避免频繁触发;useVirtualList(来自 @vueuse/core)已内置这些优化,可直接封装复用
缓存和性能关键点
避免重复测量和无效重算:











