应用 Tanstack Virtual

profile image

通过 Tanstack Virtual 实现列表虚拟化,优化 DOM!

本帖由 DeepL 翻译。如有任何翻译错误,请告知我们!

什么是 Tanstack Virtual?

Tanstack Virtual 是一个用于高效渲染大型滚动元素的虚拟化库。

tanstack-virtual-example1

无论有多少元素,它都只在实际 DOM 中渲染固定数量的元素。随着 DOM 中渲染的元素数量增加,性能会在某个点急剧下降,因此当需要显示大量元素时,可以应用这种虚拟化技术。

除了 Tanstack 外,还有其他虚拟化库,如 react-virtualized 和 react-window。

问题

Image.png

以前,它是按上图所示的网格结构组织的,显示的卡片数量根据视口而变化。

从 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 分割,将现有的项目列表从一维数组转换为二维数组。

看图表,感觉是这样的。

Image.png

需要注意的是确保每行只有一个元素,以及在该元素中放入多少项!

实际代码如下。

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 中,如下所示!

tanstack-virtual-example2

❤️ 0
🔥 0
😎 0
⭐️ 0
🆒 0