优化 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 中的组件。
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
函数移到渲染函数之外,这样它就不会在每次调用渲染函数时重新创建自己。
const renderItem = useCallback(({item}) => (
<View key={item.key}>
<Text>{item.title}</Text>
</View>
), []);
return (
// ...
<FlatList data={items} renderItem={renderItem} />;
// ...
);