跳到主要内容

编写高性能组件

虽然 FlashList 尽力实现高性能,但如果你的项目组件渲染速度缓慢,它的性能仍然会很差。在这篇文章中,让我们深入探讨如何解决这个问题。

回收

要理解的一个重要事情是 FlashList 在底层是如何工作的。当一个项目超出视口时,该组件不会被销毁,而是使用不同的 item 属性重新渲染。在优化你的项目组件时,尽量确保在回收时需要重新渲染和重新计算的东西尽可能少。

优化

有很多适用于任何 React Native 组件的优化方法,这些方法也可能有助于提高项目组件的渲染时间。建议使用 useCallbackuseMemouseRef - 但不要盲目使用这些,始终在进行更改前后测量性能

注意

始终在发布模式下分析性能。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 属性应该从 MyItemMyNestedComponent 中移除

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} />;
};

但是,此实现有一个性能缺点。当列表回收项目并且 MessageTypeText 更改为 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>
</>
);
};