编写高性能组件
虽然 FlashList 尽力实现高性能,但如果你的项目组件渲染速度缓慢,它的性能仍然会很差。在这篇文章中,让我们深入探讨如何解决这个问题。
回收
要理解的一个重要事情是 FlashList 在底层是如何工作的。当一个项目超出视口时,该组件不会被销毁,而是使用不同的 item 属性重新渲染。在优化你的项目组件时,尽量确保在回收时需要重新渲染和重新计算的东西尽可能少。
优化
有很多适用于任何 React Native 组件的优化方法,这些方法也可能有助于提高项目组件的渲染时间。建议使用 useCallback、useMemo 和 useRef - 但不要盲目使用这些,始终在进行更改前后测量性能。
注意
始终在发布模式下分析性能。FlashList 在 JS 开发模式和发布模式之间的性能差异很大。
estimatedItemSize
确保 estimatedItemSize 尽可能接近实际平均值 - 请参阅此处,了解如何正确计算此属性的值。
移除 key 属性
警告
在你的项目和项目嵌套组件中使用 key 属性会严重降低性能。
请确保你的项目组件及其嵌套组件没有 key 属性。使用此属性会导致 FlashList 无法回收视图,从而失去使用它而不是 FlatList 的所有好处。
例如,如果我们有以下项目组件
const MyNestedComponent = ({ item }) => {
return <Text key={item.id}>I am nested!</Text>;
};
const MyItem = ({ item }) => {
return (
<View key={item.id}>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};
那么 key 属性应该从 MyItem 和 MyNestedComponent 中移除
const MyNestedComponent = ({ item }) => {
return <Text>I am nested!</Text>;
};
const MyItem = ({ item }) => {
return (
<View>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};
在某些情况下,React 会强制你使用 key 属性,例如在使用 map 时。在这种情况下,请确保 key 不以任何方式与 item 属性绑定,这样键在回收时就不会更改。
假设我们要显示用户的姓名
const MyItem = ({ item }: { item: any }) => {
return (
<>
{item.users.map((user: any) => {
<Text key={user.id}>{user.name}</Text>;
})}
</>
);
};
如果我们像这样编写我们的项目组件,则需要重新创建 Text 组件。相反,我们可以这样做
const MyItem = ({ item }) => {
return (
<>
{item.users.map((user, index) => {
/* eslint-disable-next-line react/no-array-index-key */
<Text key={index}>{user.name}</Text>;
})}
</>
);
};
虽然 React 不建议在 map 中使用索引作为 key,但在这种情况下,由于数据来自列表的数据,因此项目将正确更新。
复杂的计算
如果你进行任何可能占用大量资源的计算,请考虑将其记忆化,使其更快或完全删除。项目的渲染方法应尽可能高效
getItemType
如果你有不同类型的单元格组件,并且这些组件差异很大,请考虑利用 getItemType 属性。例如,如果我们正在构建一个消息列表,我们可以这样编写
// A message can be either a text or an image
enum MessageType {
Text,
Image,
}
interface TextMessage {
text: string;
type: MessageType.Text;
}
interface ImageMessage {
image: ImageSourcePropType;
type: MessageType.Image;
}
type Message = ImageMessage | TextMessage;
const MessageItem = ({ item }: { item: Message }) => {
switch (item.type) {
case MessageType.Text:
return <Text>{item.text}</Text>;
case MessageType.Image:
return <Image source={item.image} />;
}
};
// Rendering the actual messages list
const MessageList = () => {
return <FlashList renderItem={MessageItem} estimatedItemSize={200} />;
};
但是,此实现有一个性能缺点。当列表回收项目并且 MessageType 从 Text 更改为 Image 或反之时,React 将无法优化重新渲染,因为项目组件的整个渲染树都会发生变化。我们可以通过将 MessageList 更改为以下内容来解决此问题
const MessageList = () => {
return (
<FlashList
renderItem={MessageItem}
estimatedItemSize={200}
getItemType={(item) => {
return item.type;
}}
/>
);
};
FlashList 现在将根据 item.type 使用单独的回收池。这意味着我们永远不会回收不同类型的项目,从而使重新渲染更快。
叶子组件
让我们考虑以下示例
const MyHeavyComponent = () => {
return ...;
};
const MyItem = ({ item }) => {
return (
<>
<MyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};
由于 MyHeavyComponent 不直接依赖于 item 属性,因此可以使用 memo 来跳过在项目回收并因此重新渲染时重新渲染 MyHeavyComponent
const MyHeavyComponent = () => {
return ...;
};
const MyItem = ({ item }: { item: any }) => {
const MemoizedMyHeavyComponent = memo(MyHeavyComponent);
return (
<>
<MemoizedMyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};