性能优化--列表虚拟化

1,025 阅读2分钟

列表虚拟化

“虚拟化”项目列表涉及维护一个窗口在列表中移动该窗口。react-virtualized 中的窗口通过以下方式工作:

  • 具有相对定位(窗口)的小型容器 DOM 元素(例如ul)
  • 有一个用于滚动的大 DOM 元素
  • 绝对定位容器内的子元素,设置它们的顶部、左侧、宽度和高度样式。

虚拟化不是一次渲染列表中的 1000 个元素(这会导致初始渲染速度变慢或影响滚动性能),而是专注于仅渲染用户可见的项目

react-window

是对react-virtualized 的重写,react-window 往往更简单。react-window 的组件包括:

List

列表呈现元素的窗口列表(行), 这意味着仅向用户显示可见行(例如FixedSizeListVariableSizeList)。List使用Grid (内部)呈现行,将Props传到到该Gird。

使用 react-window 渲染列表

Row和Column实现方式类似:(FixedSizeList)

import { FixedSizeList as List } from 'react-window';
 
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);
html {
  font-family: sans-serif;
  font-size: 12px;
}

.List {
  border: 1px solid #d9dddd;
}

.ListItemEven,
.ListItemOdd {
  display: flex;
  align-items: center;
  justify-content: center;
}

.ListItemEven {
  background-color: #f8f8f0;
}

Gird

Grid沿垂直和水平轴(例如FizedSizeGridVariableSizeGid )呈现数据,虚拟化。它仅根据当前水平/垂直滚动位置呈现填充自身所需的 Grid 单元格。

import { VariableSizeGrid as Grid } from 'react-window';
 
// These item sizes are arbitrary.
// Yours should be based on the content of the item.
const columnWidths = new Array(1000)
  .fill(true)
  .map(() => 75 + Math.round(Math.random() * 50));
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));
 
const Cell = ({ columnIndex, rowIndex, style }) => (
  <div style={style}>
    Item {rowIndex},{columnIndex}
  </div>
);
 
const Example = () => (
  <Grid
    columnCount={1000}
    columnWidth={index => columnWidths[index]}
    height={150}
    rowCount={1000}
    rowHeight={index => rowHeights[index]}
    width={300}
  >
    {Cell}
  </Grid>
);

react-window-infinite-loader

帮助将大型数据集分解成块,这些块可以在滚动到视图中时及时加载。

import React, { Component } from 'react';
import { FixedSizeGrid as Grid } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
...
  render() {
    return (
      <InfiniteLoader
        isItemLoaded={this.isItemLoaded}
        loadMoreItems={this.loadMoreItems}
        itemCount={this.state.count + 1}
      >
        {({ onItemsRendered, ref }) => (
          <Grid
            onItemsRendered={this.onItemsRendered(onItemsRendered)}
            columnCount={COLUMN_SIZE}
            columnWidth={180}
            height={800}
            rowCount={Math.max(this.state.count / COLUMN_SIZE)}
            rowHeight={220}
            width={1024}
            ref={ref}
          >
            {this.renderCell}
          </Grid>
        )}
      </InfiniteLoader>
    );
  }
}

react-window 还没有 react-virtualized 的完整 API 界面,所以如果考虑的话,请检查比较文档。少了什么东西?

  • WindowScroller - 这是一个react-virtualized允许列表根据窗口的滚动位置滚动的组件。目前没有计划为 react-window 实现此功能,因此您需要在用户空间中解决此问题。
  • AutoSizer - HOC 增长以适应所有可用空间,自动调整单个子项的宽度和高度
  • CellMeasurer - HOC 通过以用户不可见的方式呈现单元格的内容来自动测量单元格的内容。

也就是说,我们发现 react-window 足以满足我们的大部分需求,它包含开箱即用的内容。