场景组件
场景组件总览
react-virtuoso 内置了几个"专场景组件",一个库覆盖绝大多数虚拟化需求:
| 组件 | 场景 |
|---|---|
Virtuoso | 通用列表(动态高度 / 无限加载 / 聊天) |
VirtuosoGrid | 网格 / 瀑布流(多列、不定宽) |
TableVirtuoso | 表格(海量行 + 吸顶表头) |
GroupedVirtuoso | 分组列表(吸顶组头) |
场景 1:VirtuosoGrid(瀑布流 / 网格)
import { VirtuosoGrid } from 'react-virtuoso';
<VirtuosoGrid
style={{ height: '500px' }}
data={photos}
listClassName="grid" // 网格容器类名(用 className,不用 components)
itemClassName="grid-item" // 每一项类名
itemContent={(index, photo) => <img src={photo.url} loading="lazy" />}
/>
.grid { display: flex; flex-wrap: wrap; }
.grid-item { width: 33.33%; box-sizing: border-box; padding: 4px; }
.grid-item img { width: 100%; display: block; }
要点:网格布局靠 CSS(flex-wrap / grid),virtuoso 只负责"只渲染视口附近的行"。
场景 2:TableVirtuoso(海量表格 + 吸顶表头)
import { TableVirtuoso } from 'react-virtuoso';
<TableVirtuoso
style={{ height: '400px' }}
data={orders}
fixedHeaderContent={() => ( // 吸顶表头
<tr>
<th>订单号</th>
<th>金额</th>
<th>状态</th>
</tr>
)}
itemContent={(index, order) => (
<>
<td>{order.no}</td>
<td>{order.amount}</td>
<td>{order.status}</td>
</>
)}
/>
- 自动产出
<table>,表头fixedHeaderContent吸顶; - 10 万行 × 20 列这种规模也能保持流畅(
computeItemKey用order.id)。
场景 3:GroupedVirtuoso(分组 + 吸顶组头)
import { GroupedVirtuoso } from 'react-virtuoso';
const groupCounts = [3, 5, 2]; // 每组各有多少条
const data = [...]; // 拍平的完整数据
<GroupedVirtuoso
style={{ height: '400px' }}
groupCounts={groupCounts}
groupContent={(index, group) => (
<div className="group-header">分组 {index + 1}(吸顶)</div>
)}
itemContent={(index, groupIndex, item) => (
<div>{item.name}</div>
)}
/>
适合通讯录按首字母分组、订单按状态分组这类场景,组头滚动时吸顶。
场景 4:聊天消息流(反向 + 跟随最新)
聊天的特殊性:从底部开始、新消息自动滚到底、往上翻加载历史。
import { useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
function Chat() {
const [messages, setMessages] = useState(initialMessages);
const [atBottom, setAtBottom] = useState(true);
const sendMessage = (text) => {
setMessages((prev) => [...prev, { id: Date.now(), text }]);
};
return (
<Virtuoso
style={{ height: '400px' }}
data={messages}
followOutput={atBottom ? 'smooth' : false} // 在底部才平滑跟随新消息
atBottomStateChange={(isAtBottom) => setAtBottom(isAtBottom)} // 记录是否在底部
itemContent={(index, msg) => <MessageBubble msg={msg} />}
/>
);
}
要点:
followOutput:用户在底部时新消息自动平滑滚到底;用户往上翻历史时不打扰;atBottomStateChange:同步"是否在底部",配合followOutput判断要不要跟随;- 往上加载历史:用
firstItemIndex(给已加载的最早一条一个初始 index)+startReached在顶部加载更早的消息; - 每条消息高度不同(长文本 / 图片),自动测量天然适配。
场景清单
| 需求 | 用哪个 | 关键 prop |
|---|---|---|
| 通用长列表 | Virtuoso | data / itemContent |
| 无限加载 feed | Virtuoso | endReached |
| 瀑布流 / 多列 | VirtuosoGrid | listClassName + CSS |
| 海量表格 + 吸顶表头 | TableVirtuoso | fixedHeaderContent |
| 分组列表 + 吸顶组头 | GroupedVirtuoso | groupCounts / groupContent |
| 聊天消息流 | Virtuoso | followOutput / firstItemIndex |