跳至主要内容

React Native 中更佳的列表视图

·6 分钟阅读
Spencer Ahrens
Facebook 软件工程师

在我们在社区群组中发布的预告公告之后,你们中的许多人已经开始尝试使用我们的一些新的列表组件了,但我们今天正式宣布它们!不再有 ListViewDataSource、过时的行、被忽略的错误或过度的内存消耗 - 使用最新的 React Native 2017 年 3 月发布候选版本 (0.43-rc.1),您可以从新的组件套件中选择最适合您用例的组件,并获得开箱即用的出色性能和功能集

<FlatList>

这是用于简单、高性能列表的主力组件。提供一个数据数组和一个 renderItem 函数,您就可以开始了。

<FlatList
data={[{title: 'Title Text', key: 'item1'}, ...]}
renderItem={({item}) => <ListItem title={item.title} />}
/>

<SectionList>

如果您想渲染一组数据,将其分解成逻辑部分,也许带有节标题(例如在按字母顺序排列的地址簿中),并且可能具有异构数据和渲染(例如,带有某些按钮的个人资料视图,然后是撰写器,然后是照片网格,然后是好友网格,最后是故事列表),这就是要走的路。

<SectionList
renderItem={({item}) => <ListItem title={item.title} />}
renderSectionHeader={({section}) => <H1 title={section.key} />}
sections={[ // homogeneous rendering between sections
{data: [...], key: ...},
{data: [...], key: ...},
{data: [...], key: ...},
]}
/>

<SectionList
sections={[ // heterogeneous rendering between sections
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
]}
/>

<VirtualizedList>

幕后实现,具有更灵活的 API。如果您的数据不在普通数组中(例如不可变列表),则特别方便。

功能

列表在许多上下文中使用,因此我们将新的组件打包了丰富的功能,以开箱即用地处理大多数用例。

  • 滚动加载 (onEndReached)。
  • 下拉刷新 (onRefresh / refreshing)。
  • 可配置 可见性 (VPV) 回调 (onViewableItemsChanged / viewabilityConfig)。
  • 水平模式 (horizontal)。
  • 智能项目和节分隔符。
  • 多列支持 (numColumns)
  • scrollToEndscrollToIndexscrollToItem
  • 更好的 Flow 类型。

一些注意事项

  • 当内容滚动出渲染窗口时,项目子树的内部状态不会保留。确保所有数据都捕获在项目数据或外部存储(如 Flux、Redux 或 Relay)中。

  • 这些组件基于 PureComponent,这意味着如果 props 保持浅层相等,它们将不会重新渲染。确保 renderItem 函数直接依赖的所有内容都作为 prop 传递,并且在更新后不为 ===,否则您的 UI 在更改时可能不会更新。这包括 data prop 和父组件状态。例如

    <FlatList
    data={this.state.data}
    renderItem={({item}) => (
    <MyItem
    item={item}
    onPress={() =>
    this.setState(oldState => ({
    selected: {
    // New instance breaks `===`
    ...oldState.selected, // copy old data
    [item.key]: !oldState.selected[item.key], // toggle
    },
    }))
    }
    selected={
    !!this.state.selected[item.key] // renderItem depends on state
    }
    />
    )}
    selected={
    // Can be any prop that doesn't collide with existing props
    this.state.selected // A change to selected should re-render FlatList
    }
    />
  • 为了限制内存并启用平滑滚动,内容会异步地渲染到屏幕外。这意味着滚动速度可能快于填充率,并可能短暂看到空白内容。这是一个可以根据每个应用程序的需求进行调整的权衡,我们正在幕后努力改进它。

  • 默认情况下,这些新列表会在每个项目上查找 key prop 并将其用于 React 密钥。或者,您可以提供自定义 keyExtractor prop。

性能

除了简化 API 之外,新的列表组件还具有显著的性能增强,主要体现在无论行数多少,内存使用量几乎保持不变。这是通过“虚拟化”渲染窗口之外的元素来实现的,方法是将它们完全从组件层次结构中卸载并从 React 组件中回收 JS 内存,以及从阴影树和 UI 视图中回收本机内存。这有一个缺点,即内部组件状态将不会保留,因此**请确保在组件本身之外跟踪任何重要的状态,例如在 Relay 或 Redux 或 Flux 存储中**。

限制渲染窗口还可以减少 React 和本机平台需要执行的工作量,例如视图遍历。即使您正在渲染一百万个元素中的最后一个,使用这些新列表也不需要迭代所有这些元素才能进行渲染。您甚至可以跳到中间,使用 scrollToIndex 而无需过度渲染。

我们还在调度方面进行了一些改进,这应该有助于提高应用程序的响应能力。渲染窗口边缘的项目很少渲染,并且在任何活动手势、动画或其他交互完成后,其优先级较低。

高级用法

ListView 不同,渲染窗口中的所有项目都会在任何 prop 发生更改时重新渲染。通常情况下,这很好,因为窗口化将项目数量减少到一个常数,但是如果您的项目比较复杂,您应该确保遵循 React 的性能最佳实践,并在您的组件中根据需要使用 React.PureComponent 和/或 shouldComponentUpdate 来限制递归子树的重新渲染。

如果您可以在不渲染的情况下计算行的高度,则可以通过提供 getItemLayout prop 来改善用户体验。这使得使用例如 scrollToIndex 滚动到特定项目更加流畅,并且会改善滚动指示器 UI,因为可以在不渲染的情况下确定内容的高度。

如果您有其他数据类型,例如不可变列表,<VirtualizedList> 是要走的路。它接收一个 getItem prop,允许您返回任何给定索引的项目数据,并且具有更宽松的 Flow 类型。

如果您有特殊的用例,还可以调整许多参数。例如,您可以使用windowSize在内存使用量和用户体验之间进行权衡,使用maxToRenderPerBatch调整填充率与响应速度,使用onEndReachedThreshold控制滚动加载何时发生,等等。

未来工作

  • 现有界面的迁移(最终弃用ListView)。
  • 根据需要添加更多功能(请告诉我们!)。
  • 支持粘性节标题。
  • 更多性能优化。
  • 支持具有状态的功能性项目组件。