跳到主要内容

优化 Flatlist 配置

术语

  • VirtualizedList:FlatList 背后的组件(React Native 对 虚拟列表 概念的实现)。

  • 内存消耗:列表中有多少信息存储在内存中,这可能导致应用崩溃。

  • 响应能力:应用程序响应交互的能力。例如,低响应能力是指当你触摸一个组件时,它需要等待一段时间才能响应,而不是立即响应。

  • 空白区域:VirtualizedList 无法足够快地渲染你的项目时,你可能会在列表中遇到未渲染的组件,它们显示为空白区域。

  • 视口:内容被渲染成像素的可见区域。

  • 窗口:项目应被挂载的区域,通常远大于视口。

属性

以下是一些有助于提升 FlatList 性能的属性列表

removeClippedSubviews

类型默认值
布尔值

如果为 true,则视口之外的视图将从原生视图层级结构中分离。

优点:通过将视口之外的视图排除在原生渲染和绘制遍历之外,这可以减少主线程上的时间,从而降低丢帧的风险。

缺点:请注意,此实现可能存在 bug,例如内容缺失(主要在 iOS 上观察到),特别是当你使用变换和/或绝对定位进行复杂操作时。另请注意,这并不能显著节省内存,因为视图并未被释放,只是被分离了。

maxToRenderPerBatch

类型默认值
数字10

这是一个 VirtualizedList 属性,可以通过 FlatList 传递。它控制每次滚动时渲染的项目数量,即下一批渲染的项目块。

优点:设置一个更大的数字意味着滚动时视觉空白区域更少(增加了填充率)。

缺点:每批次更多项目意味着更长的 JavaScript 执行时间,可能阻塞其他事件处理(如点击),从而损害响应能力。

updateCellsBatchingPeriod

类型默认值
数字50

虽然 maxToRenderPerBatch 表示每批次渲染的项目数量,但设置 updateCellsBatchingPeriod 会告诉你的 VirtualizedList 批次渲染之间的毫秒延迟(你的组件渲染窗口内项目的频率)。

优点:将此属性与 maxToRenderPerBatch 结合使用,你可以例如,在不频繁的批次中渲染更多项目,或者在频繁的批次中渲染更少项目。

缺点:不频繁的批次可能导致空白区域,更频繁的批次可能导致响应性问题。

initialNumToRender

类型默认值
数字10

初始渲染的项目数量。

优点:为每台设备定义精确覆盖屏幕的项目数量。这可以大大提升初始渲染的性能。

缺点:设置过低的 initialNumToRender 可能导致空白区域,特别是如果它太小而无法在初始渲染时覆盖视口。

windowSize

类型默认值
数字21

此处传递的数字是一个测量单位,其中 1 等同于你的视口高度。默认值为 21(上方 10 个视口,下方 10 个,以及中间一个)。

优点:更大的 windowSize 将减少滚动时出现空白区域的可能性。另一方面,更小的 windowSize 将导致同时挂载的项目更少,从而节省内存。

缺点:对于更大的 windowSize,内存消耗会更多。对于更小的 windowSize,出现空白区域的可能性更大。

列表项

以下是一些关于列表项组件的提示。它们是列表的核心,因此需要快速运行。

使用基本组件

你的组件越复杂,它们的渲染速度就越慢。尝试避免在列表项中包含过多逻辑和嵌套。如果你的应用程序中大量复用此列表项组件,请专门为你的大型列表创建一个组件,并使其包含尽可能少的逻辑和嵌套。

使用轻量级组件

你的组件越重,它们的渲染速度就越慢。避免使用大型图片(对列表项使用裁剪版本或缩略图,尽可能小)。与你的设计团队沟通,在列表中使用尽可能少的效果、交互和信息。在项目详情中显示它们。

使用 memo()

React.memo() 创建一个记忆化的组件,该组件仅在传递给组件的属性更改时才重新渲染。我们可以使用此函数来优化 FlatList 中的组件。

tsx
import React, {memo} from 'react';
import {View, Text} from 'react-native';

const MyListItem = memo(
({title}: {title: string}) => (
<View>
<Text>{title}</Text>
</View>
),
(prevProps, nextProps) => {
return prevProps.title === nextProps.title;
},
);

export default MyListItem;

在此示例中,我们确定 MyListItem 仅在标题更改时才应重新渲染。我们将比较函数作为第二个参数传递给 React.memo(),以便组件仅在指定的属性更改时才重新渲染。如果比较函数返回 true,则组件将不会重新渲染。

使用缓存优化的图片

你可以使用社区包(例如 react-native-fast-image 来自 @DylanVann)以获得更高性能的图片。列表中的每张图片都是一个 new Image() 实例。它越快达到 loaded 钩子,你的 JavaScript 线程就越快再次空闲。

使用 getItemLayout

如果你的所有列表项组件具有相同的高度(或宽度,对于水平列表),提供 getItemLayout 属性可以消除你的 FlatList 管理异步布局计算的需要。这是一种非常理想的优化技术。

如果你的组件具有动态大小并且你确实需要性能,请考虑咨询你的设计团队,他们是否可以考虑重新设计以获得更好的性能。

使用 keyExtractor 或 key

你可以将 keyExtractor 设置为你的 FlatList 组件。此属性用于缓存,并作为 React key 来跟踪项目重新排序。

你也可以在你的项目组件中使用 key 属性。

避免在 renderItem 中使用匿名函数

对于函数组件,将 renderItem 函数移到返回的 JSX 之外。此外,确保它被包裹在 useCallback 钩子中,以防止每次渲染时都被重新创建。

对于类组件,将 renderItem 函数移到渲染函数之外,这样它就不会在每次调用渲染函数时重新创建自己。

tsx
const renderItem = useCallback(({item}) => (
<View key={item.key}>
<Text>{item.title}</Text>
</View>
), []);

return (
// ...

<FlatList data={items} renderItem={renderItem} />;
// ...
);