本文由 简悦 SimpRead 转码,原文地址 www.callstack.com
了解如何使用专用的、排序更高的 React Native 组件来改善用户体验和 ......
下面这篇文章的重点是React Native 优化终极指南 战术的第二点:为某些布局使用专用组件。
为什么我们认为这方面很重要?
使用专用组件总是能让您的应用程序尽可能快地运行。它对应用程序的性能有重大影响,从而带来更好的用户体验。
此外,通过应用下面文章中介绍的方法,您将提高产品的创收效率并增加投资回报率。 更重要的是,在本文中,我们将告诉您如何节省大量时间,从头开始重新实现最常见的用户界面模式。这样,您就能更快地实现未来的更新。
在基于《React Native 优化终极指南》的其他博文中,我们将讨论以下与通过了解 React Native 实现细节来提高性能相关的主题:
- 为您的应用程序选择正确的外部库
- 使用移动专用库优化电池消耗
- 在本机和 JavaScript 之间找到平衡
- 以 60FPS 的速度制作动画
- 优化 React Native 应用程序的 JavaScript 捆绑程序
现在,让我们来了解如何使用高阶 React Native 组件来实现应用程序的最佳性能。
React Native 中的原始组件和高阶组件
在 React Native 应用程序中,一切都是组件。在组件层次结构的末端,有所谓的原始组件,如文本(Text)、视图(View)或文本输入(TextInput)。这些组件由 React Native 实现,并由目标平台提供,以支持最基本的用户交互。
在构建应用程序时,我们会将其分解成更小的构件。为此,我们使用原始组件。例如,为了创建登录屏幕,我们会使用一系列 TextInput 组件来注册用户详细信息,并使用 Touchable 组件来处理用户交互。从我们在应用程序中创建的第一个组件开始,这种方法就一直适用于整个开发的最后阶段。
在原始组件的基础上,React Native 提供了一系列高阶组件,这些组件经过设计和优化,可用于特定目的。不了解这些组件或未在所有地方使用它们可能会影响应用程序的性能,尤其是当您使用真实生产数据填充状态时。应用程序的不良性能可能会严重损害用户体验。因此,这可能会让客户对您的产品不满意,从而转向您的竞争对手。
高阶组件如何影响 React Native 应用程序的性能
如果您不使用专门的组件,您就会选择放弃性能改进,并在您的应用程序进入生产阶段时面临用户体验下降的风险。值得注意的是,由于模拟数据通常较小,无法反映生产数据库的大小,因此某些问题在应用程序开发过程中不会被察觉。
专业组件更全面,拥有更广泛的应用程序接口,可覆盖绝大多数移动应用场景。
始终使用专用组件,例如用于列表的 FlatList。
让我们以长列表为例。每个应用程序都包含一个列表。创建元素列表最快、最简单的方法是将 ScrollView 和 View 原始组件结合起来。
然而,当数据增长时,这样的示例很快就会陷入困境。处理大型数据集、无限滚动和内存管理正是 FlatList 背后的动机,FlatList 是 React Native 中的一个专用组件,用于显示和处理类似的数据结构。
比较基于 ScrollView 添加新列表元素的性能
import React, { useCallback, useState } from "react";
import { ScrollView, View, Text, Button, StyleSheet } from "react-native";
const objects = [
["avocado", "🥑"],
["apple", "🍏"],
["orange", "🍊"],
["cactus", "🌵"],
["eggplant", "🍆"],
["strawberry", "🍓"],
["coconut", "🥥"],
];
const getRandomItem = () => {
const item = objects[~~(Math.random() * objects.length)];
return {
name: item[0],
icon: item[1],
id: Date.now() + Math.random(),
};
};
const _items = Array.from(new Array(5000)).map(getRandomItem);
const List = () => {
const [items, setItems] = useState(_items);
const addItem = useCallback(() => {
setItems([getRandomItem()].concat(items));
}, [items]);
return (
<View style={styles.container}>
<Button title="add item" onPress={addItem} />
<ScrollView>
{items.map(({ name, icon, id }) => (
<View style={styles.itemContainer} key={id}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.icon}>{icon}</Text>
</View>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 30,
},
itemContainer: {
borderWidth: 1,
margin: 3,
padding: 5,
flexDirection: "row",
},
name: {
fontSize: 20,
width: 150,
},
icon: {
fontSize: 20,
},
});
export default List;
和基于 FlatList 的列表的性能比较。
import React, { useCallback, useState } from "react";
import { View, Text, Button, FlatList, StyleSheet } from "react-native";
const objects = [
"avocado 🥑",
"apple 🍏",
"orage 🍊",
"cactus 🌵",
"eggplant 🍆",
"strawberry 🍓",
"coconut 🥥",
];
const getRandomItem = () => {
const item = objects[~~(Math.random() * objects.length)].split(" ");
return {
name: item[0],
icon: item[1],
id: Date.now() + Math.random(),
};
};
const _items = Array.from(new Array(5000)).map(getRandomItem);
const List = () => {
const [items, setItems] = useState(_items);
const addItem = useCallback(() => {
setItems([getRandomItem()].concat(items));
}, [items]);
const keyExtractor = useCallback(({ id }) => id.toString(), []);
const renderItem = useCallback(
({ item: { name, icon } }) => (
<View style={styles.itemContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.icon}>{icon}</Text>
</View>
),
[]
);
return (
<View style={styles.container}>
<Button title="add item" onPress={addItem} />
<FlatList
data={items}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 30,
},
itemContainer: {
borderWidth: 1,
margin: 3,
padding: 5,
flexDirection: "row",
},
name: {
fontSize: 20,
width: 150,
},
icon: {
fontSize: 20,
},
});
export default List;
区别很大,不是吗?在提供的 5000 个列表项的示例中,ScrollView 版本甚至无法平滑滚动。
说到底,FlatList 也使用了 ScrollView 和 View 组件,这又是怎么回事呢?
关键在于 FlatList 组件中抽象出来的逻辑。它包含大量启发式方法和高级 JavaScript 计算,以减少在屏幕上显示数据时发生的无关渲染次数,并使滚动体验始终以 60 FPS 运行。
在某些情况下,仅仅使用 FlatList 可能还不够。FlatList 性能优化的关键在于不呈现当前屏幕上未显示的元素。这个过程中成本最高的部分是布局测量。FlatList 必须测量布局,以确定滚动区域应为即将显示的元素保留多少空间。
对于复杂的列表元素,这可能会大大降低与平面列表的交互速度。每次 FlatList 要渲染下一批数据时,都必须等待所有新项目渲染完毕,以测量它们的高度。
不过,您可以实现 getItemHeight() 来预先定义元素高度,而无需进行测量。对于没有恒定高度的项目来说,这样做并不直接。您可以根据文本行数和其他布局限制来计算值。我们建议使用 react-native-text-size 库一次性计算所有列表项显示文本的高度。在我们的案例中,它大大提高了 FlatList Android 滚动事件的响应速度。
FlashList 作为 FlatList 的后续版本
如前所述,与 ScrollView 相比,FlatList 大幅提高了庞大列表的性能。尽管 FlatList 被证明是一种性能出色的解决方案,但它也有一些注意事项。
例如,开发人员或用户几乎每天都会遇到滚动时出现空白、滚动迟缓以及列表不流畅等问题。FlatList 的设计目的是将某些元素保留在内存中,这会增加设备的开销,最终导致列表速度减慢,而当 FlatList 无法以足够快的速度呈现项目时,就会出现空白区域。
不过,我们可以根据 此处 的提示在一定程度上减少这些问题,但在大多数情况下,我们还是希望列表更流畅、更迅速。使用 FlatList 时,JS 线程大部分时间都很忙,我们总是希望在滚动列表时,JS 线程能有 60FPS 标签。
那么我们应该如何解决这些问题呢?如果不使用 FlatList,又该怎么办呢?幸运的是,Shopify 的朋友们开发出了 FlatList 的理想替代品,即 FlashList。该库工作于RecyclerListView之上,利用其回收功能,解决了常见的痛点,如复杂的 API、使用具有动态高度的单元格或首次渲染布局不一致。
FlashList 可回收视口外的视图,并将其重新用于其他项目。如果列表中有不同的项目,FlashList 会根据项目的类型使用回收池。至关重要的是,要尽可能减少列表项的数量,并且不产生任何副作用;否则,这将损害列表的性能。
在 FlashList 中,有几个道具非常重要。首先是估计项目大小(estimateItemSize),即列表项目的大致大小。它可以帮助 FlashList 决定在初始加载前和滚动时渲染多少个项目。如果有不同大小的项目,我们可以将其平均。如果我们没有在第一次呈现时提供该值,我们可以通过列表的警告来获取该值,然后再向前使用。另一种方法是使用 React Native 应用程序中开发支持的元素检查器。
第二个道具是 overrideItemLayout,它的优先级高于 estimatedItemSize。如果我们有不同大小的项目,并且我们知道它们的大小,那么最好在这里使用它们,而不是平均它们的大小。
下面我们来谈谈 FlashList 的测量。记得事先打开 JS bundle 的发布模式。在开发模式下,FlashList 可能看起来比 FlatList 慢。主要原因是窗口大小(windowSize)更小且固定不变。我们可以利用 FlashList 内置的回调函数来测量空白区域 onBlankArea 和列表加载时间 onLoad。您可以在文档的 Metrics 部分 中阅读更多有关可用助手的信息。
我们还可以使用 Bamlab's Android Performance Profiler,它能以性能报告的形式为我们提供发布版本的 FPS 结果。它还能绘制出剖析期间 CPU 使用率的漂亮图表,因此我们可以验证某些操作对这一指标的影响。
来自 Bamlab Android 性能剖析器的性能报告
还有一个 React Native Performance Monitor Flipper 插件。我们可以用它来测量调试构建中 JS 和 UI 线程的 FPS。请确保在剖析时关闭 DEV 模式。
您的应用程序将运行得更快、显示更复杂的数据结构,而且您还可以选择进一步改进。
由于使用了专用组件,您的应用程序将始终以最快的速度运行。您将自动选择加入 React Native 到目前为止所做的所有 性能优化,并订阅未来的更新。
同时,您还可以节省大量时间,从头开始重新实现最常见的用户界面模式。粘贴部分标题、拉动刷新--你说得出的都有。如果您选择使用 FlatList,这些功能已经默认支持。
需要性能优化帮助?
我们是 React Native 的 Facebook 官方合作伙伴。我们已经在 React Native 项目 上工作了 5 年多,为客户提供了高质量的解决方案,并为 React Native 生态系统做出了巨大贡献。我们的 开源项目 每天都在帮助成千上万的开发人员应对挑战,使他们的工作变得更加轻松。
如果您需要跨平台或 React Native 开发方面的帮助,请联系我们。我们很乐意为您提供免费咨询。