使用 TanStack Virtual 进行虚拟化
Source: Dev.to
优化性能是 React 社区的常见话题。用户期望在处理可能增长到数千条记录的大型数据集时,能够实现流畅的滚动和快速的交互。
虚拟化通过仅渲染可见的元素(加上少量缓冲区),而不是一次性加载成千上万的 DOM 元素,从而提升大列表的性能。随着用户滚动,元素会动态地被添加和移除,同时列表仍保持完整渲染数据集的外观,且不会出现性能下降。
为什么虚拟化是必不可少的
在处理大量数据集时:
- Memory consumption – 渲染大量 DOM 元素会占用相当多的内存。
- Initial rendering speed – 当 React 构建并调和一个庞大的树时,初始显示会变慢。
- Scrolling responsiveness – 处理大数据集会增加工作负载和 DOM 大小,导致滚动卡顿。
- User experience – 由于滚动和点击时响应缓慢,应用可能会给人出现故障的感觉。
虚拟化显著减少了 DOM 节点数量,通常会带来更流畅的滚动和更快的交互。实现 React 虚拟化的方法有很多,在本文中,我们将重点关注 TanStack Virtual。
Introduction to TanStack Virtual
TanStack Virtual 是一个 headless 虚拟化库。它 不 包含预构建的 UI 组件。相反,它提供虚拟化 engine(ranges、offsets、total size 和 measurement utilities),让您能够完全控制标记、样式、布局和交互。当您的列表项不仅是基本行,而是徽章、菜单、图片、按钮以及复杂设计等精细 UI 元素时,这一点尤为重要。
安装
npm install @tanstack/react-virtual
# or
yarn add @tanstack/react-virtual
# or
pnpm add @tanstack/react-virtual
Understanding TanStack Virtual Fundamentals
TanStack Virtual 并不是一个可以直接放入应用后就不管的 UI 组件库。它是一个轻量级工具,负责处理虚拟化的难点,确定:
- 哪些项目应该可见,
- 这些项目在滚动空间中应放置在哪里,
- 以及完整列表应呈现多大的尺寸(从而使滚动条自然工作)。
其余的 UI 仍需你自行构建:标记、样式、布局和交互。
现在,让我们一步一步分析它。
Source: …
心智模型(“幻象”)
虚拟列表通常包含三个部分:
- 滚动容器 – 一个具有固定高度并设置
overflow: auto的div。 - 占位元素 – 一个内部元素,其高度等于列表的总高度(
getTotalSize())。 - 虚拟项 – 仅渲染可见的项目,使用
transform: translateY(...)定位。
示例
import { useVirtualizer } from "@tanstack/react-virtual";
export function BasicVirtualList() {
const parentRef = React.useRef(null);
const items = React.useMemo(
() => Array.from({ length: 100_000 }, (_, i) => `Item ${i + 1}`),
[]
);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 44, // fixed row height
overscan: 8,
});
return (
<div ref={parentRef} style={{ height: "400px", overflow: "auto" }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map((vItem) => (
<div
key={vItem.key}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: `${vItem.size}px`,
transform: `translateY(${vItem.start}px)`,
}}
>
{items[vItem.index]}
</div>
))}
</div>
</div>
);
}
您在每次实现中都会使用的关键概念
estimateSize– 返回估计的项目高度(或水平列表的宽度)的函数。getItemKey– 当列表可能因排序、过滤或新数据而变化时,使用一致的键,以确保虚拟化保持稳定。overscan– 控制在视口上方和下方额外渲染的项目数量。更高的 overscan 可以在快速滚动时减少空白间隙,但会增加每次滚动的工作量。
除了配置之外,实际因素也会影响虚拟化在您的应用中的有效性。
Tips for Virtualisation
- 固定 vs. 可变项目高度 – 固定布局更简单;可变高度需要仔细测量和管理尺寸。
- 行状态 – 虚拟化的行在滚动时可能会挂载和卸载。确保关键数据或状态不要仅存放在行组件内部。
- 焦点与可访问性 – 当行被卸载时,如果焦点消失,输入和键盘导航可能会中断。
- 图片和动态内容 – 加载缓慢的媒体可能会改变高度,如果处理不当会导致滚动跳动。
- 性能验证 – 使用 React Profiler 和 Chrome Performance 工具比较前后结果。
常见陷阱(以及…)
此部分的内容可稍后添加。
如何避免它们
-
快速滚动时出现空白间隙
稍微增加overscan,以便在可视视口之外渲染更多项目。 -
可变内容导致的滚动跳动
通过设置固定高度或宽高比为图像预留空间,或启用精确测量,以防止动态高度变化导致的滚动位置偏移。 -
行状态丢失
将状态提升并按id存储。避免仅在行组件内部保存关键状态,因为它们在滚动时可能会挂载和卸载。 -
键错误
当列表顺序可能变化时,使用getItemKey提供稳定的键。不要依赖数组索引。
总结
虚拟化是您在 React 中对大型列表进行性能提升的最有效方法之一。使用 TanStack React Virtual,您将获得一个多功能、无 UI 框架的引擎,能够平稳地集成到实际应用中——尤其是在您需要对 UI 布局进行完整控制时。