使用 React 虚拟化渲染列表

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

在 React 中处理数据相对容易,因为 React 旨在将数据作为状态处理。当您需要使用的数据变得巨大时,麻烦就开始了。例如,假设您必须处理一个包含 500 到 1000 条记录的数据集。这可能导致巨大的负载并导致性能问题。好吧,我们将看看如何在 React 中使用虚拟化列表来无缝地渲染应用程序中的长数据列表。

我们将使用 React Virtualized 组件来获得我们所需的内容。它将允许我们获取大型数据集,动态处理它们,并以很少或没有卡顿的方式渲染它们。

设置

React Virtualized 已经有一套详细的说明来启动和运行它,所以请 查看仓库 以开始使用。

我们将需要一些数据来处理,所以我们将设置一个使用 faker 创建大型数据集的函数。

function createRecord(count) {
  let records = [];

  for (let i = 0; i < count; i++) {
    records.push({
      username: faker.internet.userName(),
      email: faker.internet.email()
    });
  }
  return records;
}

接下来,我们将向它传递我们想要创建的数据记录的数量,如下所示

const records = createRecord(1000);

好了,现在我们有了渲染这些记录列表所需的一切!

创建虚拟化列表

这是我们想要创建的列表,不带样式。我们可以通过导入包含的 CSS 文件来使用库包含的少量表示样式,但我们将在本文中省略它。

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 React Virtualized 1

继续重新运行该演示。非常快,对吧?

您可能想知道 React Virtualized 在幕后做了什么来实现这一点。事实证明,它是一系列疯狂而酷炫的尺寸调整、定位、转换和过渡,这些允许记录在用户滚动时进出视图。数据已经存在并已渲染。React Virtualized 创建了一个窗口框架,允许记录在用户滚动时滑动进出视图。

要在 React Virtualized 中渲染虚拟化列表,我们使用其List组件,该组件在内部使用Grid组件来渲染列表。

首先,我们从设置rowRenderer开始,它负责显示单行并设置一个为每条记录分配 ID 的索引。

rowRenderer = ({ index, isScrolling, key, style }) => {
    return (
      <div key={key} style={style}>
        <div>{this.props.data[index].username}</div>
        <div>{this.props.data[index].email}</div>
      </div>
    );
  };

如您所见,这返回一个包含两个额外 div 的单个 div 节点:一个用于用户名,另一个用于电子邮件。您知道,显示用户的常见列表模式。

rowRenderer接受多个参数。以下是它们以及每个参数的作用

  • index:记录的数字 ID。
  • isScrolling:指示List组件中是否正在发生滚动。
  • isVisible:确定行是可见还是超出视图。
  • key:记录在数组中的位置。
  • parent:定义列表是另一个列表的父级还是子级。
  • style:一个用于定位行的样式对象。

现在我们对rowRenderer函数有了更多了解,让我们在List组件中使用它

<List
  rowCount={this.props.data.length}
  width={width}
  height={height}
  rowHeight={rowHeight}
  rowRenderer={this.rowRenderer}
  overscanRowCount={3}
/>

您可能已经注意到一些新的参数。以下是它们

  • rowCount:这将列表中行的数量传递给我们以计算列表的长度。
  • width:列表的宽度。
  • height:列表的高度。
  • rowHeight:这可以是一个数字或一个函数,该函数根据其索引返回行高。
  • rowRenderer:这负责渲染行。列表不应直接传递,因此我们传递在本教程中创建的rowRenderer函数。
  • overscanRowCount:这用于在用户滚动方向渲染额外的行。它减少了用户滚动速度快于虚拟化内容渲染的可能性。

最后,您的代码应如下所示:

const { List } = ReactVirtualized

...

const height = 700;
const rowHeight = 40;
const width = 800;

class App extends React.Component {
  rowRenderer = ({ index, isScrolling, key, style }) => {
    return (
      <div key={key} style={style}>
        <div>{this.props.data[index].username}</div>
        <div>{this.props.data[index].email}</div>
      </div>
    );
  };

  render() {
    return (
      <div>
        <h2>Details</h2>
        <List
          rowCount={this.props.data.length}
          width={width}
          height={height}
          rowHeight={rowHeight}
          rowRenderer={this.rowRenderer}
          overscanRowCount={3}
        />
      </div>
    );
  }
}

单元格测量器

根据文档,单元格测量器是一个 高阶组件,用于临时渲染列表。此时用户还看不到它,但数据已保存并准备显示。

为什么您应该关心这个?流行的用例是rowHeight的值是动态的情况。React Virtualized 可以渲染渲染时的行高,然后缓存该高度,因此它不再需要在数据滚动出视图时计算——无论其包含的内容如何,它始终具有正确的的的高度!

首先,我们创建我们的cache,这可以通过在组件的构造函数中使用CellMeasurerCache来完成

constructor() {
  super()
  this.cache = new CellMeasurerCache({
    fixedWidth: true,
    defaultHeight: 100
  })
}

我们在设置List组件时使用缓存;

<List
  rowCount={this.props.data.length}
  width={rowWidth}
  height={listHeight}
  deferredMeasurementCache={this.cache}
  rowHeight={this.cache.rowHeight}
  rowRenderer={this.renderRow}
  overscanRowCount={3}
/>

传递给deferredMeasurementCache的值将用于临时渲染数据,然后——当rowHeight的计算值传入时——额外的行将像它们一直存在一样流入。

接下来,我们将使用 React Virtualized 的CellMeasurer组件替换我们最初设置的作为占位符的 div,而不是在我们的rowRenderer函数中。

rowRenderer = ({ index, parent, key, style }) => {
  return (
    <CellMeasurer
      key={key}
      cache={this.cache}
      parent={parent}
      columnIndex={0}
      rowIndex={index}
    >
      <div style={style}>
        <div>{this.props.data[index].username}</div>
        <div>{this.props.data[index].email}</div>
      </div>
    </CellMeasurer>
  );
  };

现在数据已获取、缓存并准备在虚拟窗口中随时显示!

虚拟化表格

是的,所以这篇文章的主要目的是介绍列表,但是如果我们实际上想要将数据渲染到表格中而不是列表呢?React Virtualized 在这方面也为您提供了保障。在这种情况下,我们将使用TableColumn组件,这些组件内置于 React Virtualized 中。

以下是如何在我们的主要 App 组件中使用这些组件

class App extends React.Component {
  render() {
    return (
      <div>
        <h2>Details</h2>
        <Table
          width={500}
          height={300}
          headerHeight={20}
          rowHeight={40}
          rowCount={this.props.data.length}
          rowGetter={({ index }) => this.props.data[index]}
        >
          <Column
            label='Username'
            dataKey='username'
            width={100}
          />
            
          <Column
            width={200}
            label='Email'
            dataKey='email'
          />
        </Table>
      </div>
    );
  }
}

Table组件接受以下参数

  • width:表格的宽度。
  • height:表格的高度。
  • headerHeight:表格标题高度。
  • rowHeight:根据其索引的行高。
  • rowCount:这是我们希望在表格中显示的初始行数。它与我们在List组件示例中定义的想要开始的记录数量的方式相同。
  • rowGetter:根据其索引返回特定行的行数据。

如果您查看Column组件,您会注意到我们使用了一个dataKey参数。这将传递我们在dataKey中调用的每一列的数据,该数据接收该数据的唯一标识符。请记住,在我们创建随机数据的函数中,我们使用了两个键;usernameemail。这就是为什么我们的一列的dataKey设置为username,而另一列设置为email

总结

希望这篇循序渐进的文章能让你了解 React Virtualized 的功能、它如何使将大型数据集渲染成列表和表格变得非常快以及如何在项目中使用它。

我们在这里只触及了表面。该库能够处理许多其他用例,例如 在滚动时生成数据记录的占位符无限加载组件 以实时获取和缓存数据、允许箭头键浏览数据的方法以及我们甚至没有介绍过的漂亮的网格砌体布局。

这应该能让你有很多东西可以玩!

此外,该软件包维护得非常好。事实上,您可以加入 Slack 群组 以了解项目的最新动态,为其做出贡献,并与其他人建立联系。

还值得注意的是,React Virtualized 在 StackOverflow 中有自己的标签,这可以成为查找其他人提出的关于它的问题或发布您自己问题的良好资源。

哦,如果您在项目中使用了 React Virtualized,我们很乐意知道!在评论中与我们分享您如何处理它或从中了解到的内容。