VirtualList 虚拟列表
仅渲染可见项目来高效渲染大型列表。由 @tanstack/react-virtual 提供支持。
此组件需要 @tanstack/react-virtual 作为对等依赖:
npm install @tanstack/react-virtualimport { VirtualList } from 'asterui/virtuallist'基础用法
高效渲染 10,000 个项目的列表。
import { VirtualList } from 'asterui/virtuallist'
function App() {
const basicItems = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i + 1}`
}))
return (
<VirtualList
items={basicItems}
height={300}
itemHeight={40}
className="border border-base-300 rounded-lg"
renderItem={(item) => (
<div className="p-2 border-b border-base-300 flex items-center h-full">
{item.name}
</div>
)}
/>
)
}
export default App 样式列表
带有卡片样式和悬停效果。
import { VirtualList } from 'asterui/virtuallist'
function App() {
const users = Array.from({ length: 5000 }, (_, i) => ({
id: i,
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`
}))
return (
<VirtualList
items={users}
height={300}
itemHeight={60}
className="border border-base-300 rounded-lg"
renderItem={(user) => (
<div className="p-3 hover:bg-base-200 transition-colors border-b border-base-300 h-full flex flex-col justify-center">
<div className="font-medium">{user.name}</div>
<div className="text-sm text-base-content/60">{user.email}</div>
</div>
)}
/>
)
}
export default App 带间隙
项目之间有间隙。
import { VirtualList } from 'asterui/virtuallist'
function App() {
const cardItems = Array.from({ length: 1000 }, (_, i) => ({
id: i,
title: `Card ${i + 1}`,
description: 'A brief description'
}))
return (
<VirtualList
items={cardItems}
height={300}
itemHeight={72}
gap={8}
className="p-2 border border-base-300 rounded-lg"
renderItem={(item) => (
<div className="card bg-base-200 p-3 h-full flex flex-col justify-center">
<h3 className="font-bold">{item.title}</h3>
<p className="text-sm text-base-content/70">{item.description}</p>
</div>
)}
/>
)
}
export default App 滚动回调
跟踪滚动位置以进行无限加载或分析。
Scroll position: 0px
import { VirtualList } from 'asterui/virtuallist'
import { useState } from 'react'
function App() {
const scrollItems = Array.from({ length: 10000 }, (_, i) => ({ id: i }))
const [scrollTop, setScrollTop] = useState(0)
return (
<div>
<div className="mb-2 text-sm text-base-content/70">Scroll position: {Math.round(scrollTop)}px</div>
<VirtualList
items={scrollItems}
height={250}
itemHeight={40}
onScroll={setScrollTop}
className="border border-base-300 rounded-lg"
renderItem={(_, index) => (
<div className="p-2 border-b border-base-300 flex items-center h-full">
Row {index + 1}
</div>
)}
/>
</div>
)
}
export default App 可变高度(聊天)
使用 Chat 组件的不同长度的聊天消息。
import { Chat } from 'asterui'
import { VirtualList } from 'asterui/virtuallist'
function App() {
const chatMessages = [
{ id: 1, user: 'Alex', text: 'Hey!' },
{ id: 2, user: 'You', text: 'Hi Alex, what\'s up?' },
{ id: 3, user: 'Alex', text: 'Not much, just wanted to check if you\'re coming to the party on Saturday' },
{ id: 4, user: 'You', text: 'Oh yeah, I\'ll be there! What time does it start?' },
{ id: 5, user: 'Alex', text: '8pm' },
{ id: 6, user: 'You', text: 'Cool' },
{ id: 7, user: 'Alex', text: 'Can you bring some snacks? We\'re running low on chips and stuff. Maybe grab a couple bags of tortilla chips and some salsa if you can find good ones' },
{ id: 8, user: 'You', text: 'Sure thing, I\'ll stop by the store on the way' },
{ id: 9, user: 'Alex', text: 'Thanks!' },
{ id: 10, user: 'You', text: 'No problem. Who else is coming?' },
{ id: 11, user: 'Alex', text: 'Sarah, Mike, probably Jordan. Maybe a few others' },
{ id: 12, user: 'You', text: 'Nice' },
{ id: 13, user: 'Alex', text: 'Yeah it should be fun. We\'re gonna set up the projector in the backyard for a movie later if the weather holds up' },
{ id: 14, user: 'You', text: 'What movie?' },
{ id: 15, user: 'Alex', text: 'Haven\'t decided yet. Any suggestions?' },
{ id: 16, user: 'You', text: 'How about something funny? We could do a comedy' },
{ id: 17, user: 'Alex', text: 'Good idea' },
{ id: 18, user: 'You', text: 'Alright, see you Saturday then!' },
{ id: 19, user: 'Alex', text: 'See ya!' },
{ id: 20, user: 'You', text: '👋' },
]
return (
<VirtualList
items={chatMessages}
height={300}
itemHeight={(msg) => 70 + Math.floor(msg.text.length / 40) * 24}
className="bg-base-100 border border-base-300 rounded-lg p-2"
renderItem={(msg) => (
<Chat
position={msg.user === 'You' ? 'end' : 'start'}
header={msg.user}
message={msg.text}
color={msg.user === 'You' ? 'primary' : undefined}
/>
)}
/>
)
}
export default App VirtualList
Section titled “VirtualList”| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
items | 要渲染的项目数组 | T[] | - |
height | 可滚动容器的高度 | number | string | - |
itemHeight | 固定高度,或返回可变高度的每项预估高度的函数 | number | ((item: T, index: number) => number) | - |
renderItem | 每个项目的渲染函数 | (item: T, index: number) => ReactNode | - |
overscan | 在可见区域外渲染的项目数 | number | 5 |
gap | 项目之间的间隙(像素) | number | 0 |
width | 容器的宽度 | number | string | - |
className | 滚动容器的额外类 | string | - |
innerClassName | 内部容器的额外类 | string | - |
itemClassName | 每个项目包装器的额外类 | string | - |
onScroll | 滚动位置更改时的回调 | (scrollTop: number) => void | - |
data-testid | 用于测试的测试 ID | string | - |
- 尽可能使用固定的
itemHeight以获得最佳性能 - 如果
renderItem函数开销很大,请将其记忆化 - 如果在快速滚动时看到闪烁,请增加
overscan - 保持项目组件简单 - 避免在渲染中进行繁重的计算