2023年11月06日
优化React Native中的用户体验,以及解决性能问题
一天,我所在的公司决定需要一个移动应用。我们的前端团队自愿帮助这个有趣的项目。众所周知,如果你是前端开发人员,尤其是使用React库进行开发,React Native是开发移动应用的最佳方式。这就是我们开始着手开发我们的WMS应用的方式。
随着应用的不断发展,我们开始考虑其性能检查和优化方式。
我们的计划是:
- 确定问题所在
- 测量可测量的内容(不要凭直觉)
- 分析
- 如有必要,进行改进
- 保持控制
那时,我们的应用包含两个主要部分——导航和可滚动列表。我们使用不同的工具来检查每个部分的性能。
基本上,在大多数性能检查工具中,绿色表示实际上很好,黄色表示有改进的空间,但应用仍然可以正常工作,红色表示你的应用和客户都需要帮助。
我们确定了一些问题,第一个问题主要与负责导航的抽屉组件的渲染时间有关:
React DevTools的应用分析器
我们还检查了我们应用的性能:
Flipper + RN性能监视器的应用性能
我们发现导航的性能几乎降至0。这个问题已经存在很长时间,并且在文档中有描述。简而言之,当推送一个新屏幕时,React Navigation首先在屏幕外渲染它,然后再将其动画显示到位。因此,如果有一个屏幕上有很多组件,很容易需要几百毫秒来渲染。
我们的应用内有很多嵌套的导航器,所以我们面临了这个问题。在React Navigation库发布修复之前,有一种避免这个问题的方法——不要使用太多的导航器,尽量减少嵌套。另外,你可以尝试使用InteractionManager来推迟繁重的计算。
第二个问题与我们的可滚动列表有关:
React DevTools的可滚动列表分析器
可滚动列表
在滚动过程中,我们测量了性能,结果显示如下:
Flipper + RN性能监视器的可滚动列表性能
虽然不算太糟糕,但肯定有一些地方需要优化。我们从代码和组件入手。基本上,我们有一个带有渲染数据的FlatList
组件。首先要检查的是我们如何在那里使用记忆化。例如,我们需要扫描条形码并获取特定数据,然后根据这些数据显示相关列表。如果我们扫描相同的条形码,整个屏幕会重新渲染,因为它认为有新数据进来了。为了避免这种情况,我们需要分析哪些组件经常重新渲染,并尝试使用useCallback
、useMemo
和React.memo
等方法来记忆化函数、变量和属性。但要记住,只在实际需要的地方使用,因为过度记忆化可能会进一步降低应用的性能。因此,只在需要进行昂贵计算时使用useMemo
,只在组件经常以相同的props重新渲染且组件相对较大时使用React.memo
,只在组件接受依赖于引用相等性的函数时使用useCallback
。当然,这并不是所有的使用情况,但是常见的情况。
接下来要检查的是我们可滚动列表的主要组件的实现:
<FlatList data={skuInfo} renderItem={({ item: sku }) => <SkuCard {...sku} />} keyExtractor={sku => sku.id} />
我们回到文档,复习了一下。文档建议不要在renderItem
属性中使用箭头函数。此外,renderItem
组件应该是一个记忆化的哑组件,以避免不必要的重新渲染。
// SkuCard组件的修复 export default memo(SkuCard); // FlatList的renderItem属性的修复 const renderItem = useCallback( ({ item: sku }: { item: SKUInfo }) => <SkuCard {...sku} />, [], ); // 修复后的FlatList的样子 <FlatList data={skuInfo} renderItem={renderItem} keyExtractor={sku => sku.id} />
我们在这些改变后检查了可滚动列表的分析器:
React DevTools的可滚动列表分析器
这有助于大大优化渲染部分。此外,我们还查看了性能:
Flipper + RN性能监视器的可滚动列表性能
好多了。那时,我们的应用规模还很小,随着它的发展,我们将需要更多地监控性能。
另外,记住你可以使用Shopify提供的一些不错的工具,比如FlashList
,你可以尝试在你的应用中实现。
结论
- 避免使用大量的导航器,尽量减少嵌套。
- 推迟计算和繁重渲染,使用
InteractionManager
来避免它们影响UI的性能。 - 尝试在首次渲染时为屏幕的繁重部分渲染占位符,然后在动画完成后用实际内容替换它们。
- 分析应用的不同部分,看看是React Navigation占用了大部分时间,还是你的应用组件逻辑可以优化。
- 谨慎使用外部库。在应用特定库之前和之后比较性能,决定哪个对你的应用更好。