Appearance
VirtualList 虚拟列表
TIP
translate3d 偏移: 这是虚拟列表的核心。我们并没有把 DOM 节点真的删掉再插入,而是只渲染这十几条,然后通过 CSS 把它们推到正确的位置。例如,当你滑到第 1000 行时,虽然这是 DOM 里的第 1 个元素,但它的 transformY 可能是 60000px。
TIP
缓冲区 (bufferCount): 代码中设置了 bufferCount = 5。这意味着如果你屏幕能显示 10 条,实际 DOM 会渲染 20 条(上面藏 5 条,下面藏 5 条)。当你快速滑动时,不会立刻看到白屏,体验会更像原生滚动。
TIP
Footer 的绝对定位: 在虚拟列表中,Footer 不能简单地写在列表下面,因为它会被 absolute 的列表内容覆盖。所以我们计算了 totalContentHeight,强行把 Footer 定位到列表的最底端。
基本使用
tsx
tsx
import { useState, useCallback } from "react";
import { VirtualList } from "@lite-code/shared";
// 定义数据类型
interface UserItem {
id: number;
name: string;
desc: string;
}
const App = () => {
// 1. 数据源
const [list, setList] = useState<UserItem[]>([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
// 2. 模拟加载数据
const loadMore = useCallback(() => {
if (loading) return;
setLoading(true);
setTimeout(() => {
// 模拟每次加载 50 条
const newItems: UserItem[] = [];
const startId = list.length;
for (let i = 0; i < 50; i++) {
newItems.push({
id: startId + i,
name: `用户 User - ${startId + i}`,
desc: `这是第 ${startId + i} 行数据,虚拟列表依然流畅丝滑`,
});
}
setList((prev) => [...prev, ...newItems]);
setLoading(false);
// 假设总共 200 条
if (list.length + 50 >= 2000) {
setHasMore(false);
}
}, 500);
}, [list.length, loading]);
// 3. 渲染单行 (Row Renderer)
// 注意:这个函数会被频繁调用,要保证性能
const rowRenderer = (item: UserItem) => {
return (
<div
style={{
// 确保内容高度撑满 row
height: "100%",
display: "flex",
alignItems: "center",
padding: "0 20px",
borderBottom: "1px solid var(--vp-c-gutter)",
background: "var(--vp-c-bg)", // 虚拟列表建议给背景色
}}
>
<div style={{ width: 50, fontWeight: "bold", color: "var(--vp-c-text-2)" }}>
#{item.id}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 500 }}>{item.name}</div>
<div style={{ fontSize: 12, color: "var(--vp-c-text-3)" }}>{item.desc}</div>
</div>
</div>
);
};
return (
<div style={{ padding: 40, background: "var(--vp-c-bg-alt)", height: 640 }}>
<div
style={{
width: 500,
margin: "0 auto",
borderRadius: 8,
overflow: "hidden",
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
}}
>
<div
style={{
padding: 16,
fontSize: 18,
fontWeight: 600,
borderBottom: "1px solid var(--vp-c-gutter)",
}}
>
虚拟滚动列表 ({list.length} 条)
</div>
<VirtualList
height={500} // 容器必须要有明确高度
itemHeight={60} // 必须明确告诉组件每一行多高
data={list} // 数据源
renderItem={rowRenderer} // 渲染函数
loading={loading}
hasMore={hasMore}
onLoad={loadMore}
/>
</div>
</div>
);
};
export default App;