react-window:学习如何高效地渲染大型列表

react-window 是一个 React 组件库,用于高效渲染大型列表和表格数据。其原理是仅渲染可见视口部分的数据来提升性能,从而减少初始渲染时间和内存占用,这也是虚拟化技术的一种。

另外,react-window 提供地 API 非常简单易用,你可以将它轻松集成到现有 React 项目中。不仅如此,react-window 以与 react-virtualized-auto-sizer 和 react-window-infinite-loader 等库一起使用,实现诸如自动调整大小和无限滚动的更高级的功能。

本篇我们只介绍 react-window 的基础常规使用方法。

安装

bash 复制代码
# Yarn
yarn add react-window

# NPM
npm install --save react-window

列表类型

我们已经说过,react-window 的强项在于渲染大型列表数据。列表依据不同的维度可以简单划分成 2 大类型。

首先,依据排列方向,我们可以将列表分为:垂直列表(vertical list)和水平列表(horizontal list)。

  • 垂直列表:每个列表项占据一行
  • 水平列表:整个列表在一行排列,每个列表项占据其中一列

当然,在我实际接触到的项目里,垂直列表更加普遍,水平列表就比较少用了。

其次,如果是依据列表项大小,我们可以将列表分为:固定尺寸列表(fixed size list)和可变尺寸列表(variable size list)。

当然,一个列表项同时存在宽和高 2 个维度,我们所说的固定和可变,具体要依据列表方向,所指带的是其中一个维度。具体来说:

  • 垂直列表 而言,所说的固定/可变尺寸是指列表项的高

此时,固定尺寸列表的每个项目的高度是一样的;而可变尺寸列表的每个项目的高度可能不一样。

  • 同理,对水平列表 而言,所说的固定/可变尺寸是指列表项的宽

此时,固定尺寸列表的每个项目的宽度是一样的;而可变尺寸列表的每个项目的宽度可能不一样。


在 react-window 中,固定尺寸列表和可变尺寸列表分别使用 <FixedSizeList><VariableSizeList> 组件表示。

jsx 复制代码
// 引入固定尺寸列表
import { FixedSizeList } from 'react-window';
// 引入可变尺寸列表
import { VariableSizeList } from 'react-window';

接下来,我们就来学习这 2 个组件的使用。

使用列表组件

FixedSizeList - 默认

demo 地址。编写如下代码:

jsx 复制代码
import { FixedSizeList } from 'react-window';
 
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <FixedSizeList
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </FixedSizeList>
);

FixedSizeList 的 API 很简单:

  • height 和 width 呢,就是用来指定列表容器的高和宽的,我们的列表内容肯定会超过容器的,因此会有一个 overflow: auto 设置。当然,默认 FixedSizeList 渲染的是垂直列表,你可以通过 layout prop 控制变成水平的(这块不急,接下来会介绍)

  • itemCount 和 itemSize 也是实现虚拟的必需参数,itemSize 表示列表项目尺寸是 35px,垂直列表的话这表示的是列表项目的高为 35px。列表项目的高度有了,然后再加上列表项的个数,我们就能得到这个列表的总高度,就能实现我们的虚拟滚动了。

最后,我们来查看效果。

值得注意的是,<FixedSizeList> 组件因为是通过绝对定位控制虚拟列表滚动的,因此你必须要给行组件 **<Row>** 透传来自 **<FixedSizeList>** 的 style 样式对象,否则虚拟滚动将不会正常工作

如果你没有透传 style 给 <Row>,会看到类似下面这样的问题。

FixedSizeList - 水平列表

以上我们谈论的是<FixedSizeList> 默认布局即垂直列表下的表现行为。而如果想让 fix size list 的水平排列,只需要将 layout prop 设置成 horizontal 都可以。

demo 地址。代码如下:

jsx 复制代码
import { FixedSizeList } from 'react-window';
 
const Column = ({ index, style }) => (
  <div style={style}>Column {index}</div>
);
 
const Example = () => (
  <FixedSizeList
    height={75}
    itemCount={1000}
    itemSize={100}
    layout="horizontal"
    width={300}
  >
    {Column}
  </FixedSizeList>
);

这个 demo 就比默认列表多了一个 layout="horizontal" 的设置,其次由于 FixedSizeList 的 children 不再是行的概念而是列,因为我们将 Row 更名更符合语义的 Column。

  • height 和 width 呢,就是用来控制水平列表容器的高和宽
  • itemCount 没什么好说的,itemSize 就不再表示列表项目的高了 ,而是宽,即水平列表下每个项目的宽度是 100px

查看效果:

当然,水平列表项也是通过绝对定位布局实现项目移动的。不过与垂直列表通过 top 实现上下偏移移动不同的是,水平列表项是通过 left 实现左右偏移移动的

VariableSizeList - 默认

讲完固定尺寸列表,再来看看可变尺寸列表。其使用与 FixedSizeList 类似,唯一不同的地方就是 itemSize 类型 从 number 变成 (index) => number。demo 地址

jsx 复制代码
import { VariableSizeList } from 'react-window';
 
// These row heights are arbitrary.
// Yours should be based on the content of the row.
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));
 
const getItemSize = index => rowHeights[index];
 
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <VariableSizeList
    height={150}
    itemCount={1000}
    itemSize={getItemSize}
    width={300}
  >
    {Row}
  </VariableSizeList>
);

getItemSize 用于获取对应数组索引下那个列表项目的尺寸,在本场景(垂直列表)中表示高。

来看看效果。

VariableSizeList - 水平列表

水平列表的设置一样。demo 地址

jsx 复制代码
import { VariableSizeList } from 'react-window';
 
// These column widths are arbitrary.
// Yours should be based on the content of the column.
const columnWidths = new Array(1000)
  .fill(true)
  .map(() => 75 + Math.round(Math.random() * 50));
 
const getItemSize = index => columnWidths[index];
 
const Column = ({ index, style }) => (
  <div style={style}>Column {index}</div>
);
 
const Example = () => (
  <VariableSizeList
    height={75}
    itemCount={1000}
    itemSize={getItemSize}
    layout="horizontal"
    width={300}
  >
    {Column}
  </VariableSizeList>
);

除了增加 layout="horizontal" 之外,Row 更名为水平列表下的列 Column。getItemSize 获取的就是每个列表项目的宽了。

来看看效果。

滚动时监听

有一个场景,如果你的列表项渲染成本太高。react-window 能够提供一个滚动时标识 isScrolling,你可以根据这个标识来动态渲染项目列表的 UI------比如用户滚动的比较快,那就没必要每个组件都需要实实在在计算渲染------就可以选选择在停止滚动时再计算渲染。

来看下例子。demo 地址

jsx 复制代码
import { FixedSizeList as List } from 'react-window';
 
const Row = ({ index, isScrolling, style }) => (
  <div style={style}>
    {isScrolling ? 'Scrolling' : `Row ${index}`}
  </div>
);

const Example = props => (
  <List useIsScrolling {...props}>
    {Row}
  </List>
);

为了使用滚动时标识,我们需要做 2 件事。

  1. 列表组建上通过设置 useIsScrolling 启用滚动时标识
  2. 由此,在渲染项目时,会多出一个 isScrolling 属性。当当前列表出在滚动时,isScrolling 为 true,不滚就为 false。

我们看下效果:

滚动到指定项

react-window 列表还提供了一个滚动到指定项的方法,是通过 ref 引用的方式实现。

jsx 复制代码
import { FixedSizeList as List } from "react-window";

const Row = ({ index, style }) => (
  <div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
    Row {index}
  </div>
);

const Example = () => {
  listRef = React.createRef();

  render() {
    return (
      <Fragment>
        <div>
          <button className="ExampleButton" onClick={this.scrollToRow200Auto}>
            Scroll to row 200 (align: auto)
          </button>
        </div>
        <List
          className="List"
          height={150}
          itemCount={1000}
          itemSize={35}
          ref={this.listRef}
          width={300}
        >
          {Row}
        </List>
      </Fragment>
    );
  }

  scrollToRow200Auto = () => {
    this.listRef.current.scrollToItem(200);
  };
}

看看效果:

行为表现:

  1. 当项目已在视口时,调用没放反映
  2. 当项目处在视口上方时,调用会定位在顶部
  3. 当项目处在视口下方时,调用会定位在底部

当然,scrollToItem 方法也支持传入第 2 个参数,明确指定项目定位的位置。可选项有:auto(默认)、start、end 以及 center。最常用的可能就是 center 了。

jsx 复制代码
this.listRef.current.scrollToItem(300, 'center');

再看看效果。

可以看到,不管何种场景,指定项目总是会强制居于视口中央。

总结

本文我们学习了高效渲染大型列表组件库 react-window 的使用,介绍了固定列表和可变列表的概念,并带大家学习 <FixedSizeList><VariableSizeList> 的使用;并学习 2 出可以提升使用体验的优化,滚动监听及滚动到指定项。

希望本文的内容对你的工作能有所帮助,感谢阅读。

相关推荐
仟濹3 小时前
【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】
大数据·前端·爬虫·数据挖掘·数据分析·html
小小小小宇4 小时前
前端WebWorker笔记总结
前端
小小小小宇4 小时前
前端监控用户停留时长
前端
小小小小宇5 小时前
前端性能监控笔记
前端
烛阴5 小时前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小55 小时前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步
穗余5 小时前
NodeJS全栈开发面试题讲解——P6安全与鉴权
前端·sql·xss
小蜜蜂嗡嗡6 小时前
flutter项目迁移空安全
javascript·安全·flutter
穗余7 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js
航Hang*7 小时前
WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
前端·笔记·edge·less·css3·html5·webstorm