什么是 Tanstack Virtual?
Tanstack Virtual 是一个用于高效渲染大型滚动元素的虚拟化库。
无论有多少元素,它都只在实际 DOM 中渲染固定数量的元素。随着 DOM 中渲染的元素数量增加,性能会在某个点急剧下降,因此当需要显示大量元素时,可以应用这种虚拟化技术。
除了 Tanstack 外,还有其他虚拟化库,如 react-virtualized 和 react-window。
问题
以前,它是按上图所示的网格结构组织的,显示的卡片数量根据视口而变化。
从 Tanstack Virtual 示例可以看出,使用 Tanstack Virtual 时,滚动 div 内的项目会应用绝对定位,并通过 transform 调整它们的位置。
换句话说,在现有方法中,这不仅仅是用 Virtualizer
包装父元素的问题。使用 Tanstack Virtual 时,需要确保每行或每列只有一个元素。
解决方案
现有的列表项是一维数组,只是通过映射来渲染它们。
typescript
const itemList = [item1, item2, item3, item4, item5, item6, item7];
return <div className='grid grid-cols-1 tablet:grid-cols-3 desktop: grid-cols-5'>
itemList.map(item => <Card item={item} />)
</div>
如上所述,由于一行中只应有一个元素,我将代码更改如下。
typescript
let columnSize = 3; // 1, 3, 5
const chunkedItemList = chunkArray(itemList, columnSize);
// [[item1, item2, item3], [item4, item5, item6], [item7]]
return <div>
chunkedItemList.map(list => <div className='grid grid-cols-1 tablet:grid-cols-3 desktop: grid-cols-5'>
{list.map(item => <Card item={item} />)}
</div>)
</div>
我通过按 columnSize 分割,将现有的项目列表从一维数组转换为二维数组。
看图表,感觉是这样的。
需要注意的是确保每行只有一个元素,以及在该元素中放入多少项!
实际代码如下。
typescript
const [columnSize, setColumnSize] = useState(COLUMN_SIZE.desktop);
const list = chunkArray(
flatPages(data) ?? [],
columnSize
);
const rowVirtualizer = useVirtualizer({
count: list?.length,
getScrollElement: () => document.getElementById("main-section"),
estimateSize: () => 390,
overscan: 1,
});
useEffect(() => {
if (isLabtop) setColumnSize(COLUMN_SIZE.labtop);
else if (isMobile) setColumnSize(COLUMN_SIZE.mobile);
else setColumnSize(COLUMN_SIZE.desktop);
}, [isLabtop, isMobile]);
{
rowVirtualizer.getVirtualItems()?.map((virtualRow) => {
const row = list[virtualRow.index];
if (!row) return null;
return (
<div
key={virtualRow.key}
className={`absolute top-0 left-0 w-full`}
style={{
transform: `translateY(${virtualRow.start}px)`,
}}
>
<div className="grid grid-cols-1 laptop:grid-cols-3 desktop:grid-cols-5 w-full gap-6 place-items-center items-stretch">
{row.map((item) => (
<Card
key={item.designId}
item={item}
isVault={isVault}
/>
))}
</div>
</div>
);
});
}
结果,我能够实现虚拟化,使得只有特定数量的项目实际渲染在 DOM 中,如下所示!