React Native性能优化:应该做和不应该做的
flytam Lv4

在使用一些框架例如React Native去实际开发移动端应用的时候,性能是一个重要的问题。React Native默认情况下的性能是没有问题的,但是在实际开发React Native的时候,我们也可能会遇到一些性能相关的问题。

这些问题是很难通过组件本身修复去解决的。在这篇文章中,我们会提供一些建议来优化开发React Native遇到的一些性能问题。

使用Image缓存解决方案

React Native在自带的组件库中提供了Image组件,可以用例展示图片。但是这个组件没有解决以下这些问题的开箱即用的解决方案:

  • 屏幕中渲染大量图片
  • 一般情况下性能比较低
  • 从缓存中加载性能比较低
  • 会有加载闪烁

React Native中的Image组件处理缓存图片的时候会像web 浏览器一样的行为,会可能导致上面提到的问题。可以通过使用第三方库react-native-fast-image来解决上面的这些问题。这个库在iOS和安卓上都可用并且能够有效的缓存图片

使用适当大小的图片

如果React Native APP依赖于使用大量的图像,那么优化图像对于APP的性能是很重要的。如果图片的尺寸没有得到合适的优化,渲染大量图片会导致在设备上占用大量的内存。这可能会导致APP崩溃

一些可以在React Native中有效优化图片的方案包括:

  • 使用PNG格式的图片而不是JPG
  • 使用尺寸更小的图片
  • 使用WEBP格式的图片。可以在iOS和Android平台减少29%的二进制大小。

避免不必要的渲染

React Native是基于React的库并且处理组件渲染的形式类似于React.js。因此在React中可用的优化方法也适用于React Native。一个优化方法就是避免不必要的渲染,在函数组件中可以通过使用React.memo()来完成。

React.memo是被用来进行处理记忆化(memoization)。记忆化的理念是:如果一个组件接收相同的props超过一次,它将会使用之前一次缓存的props。并且函数组件只会进行一次渲染返回jsx

例如下面Parent组件和Child组件的例子。Parent组件有一个count的state变量,每次button点击的时候更新count

当button点击的时候,即使Child组件的props属性text没有改变,每次Parent组件渲染都会造成Child组件的重新渲染。Child组件没有做任何和Parent组件有关的操作而仅仅是展示一些静态文本。这个行为可以通过把Child组件用React.memo()包着来进行优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Parent.js

const Parent = () => {
const [count, setCount] = useState(0);

return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title='Press me' onPress={() => setCount(count + 1)} />
<Child text='Placeholder text' />
</View>
);
};

// Child.js
const Child = React.Memo(({ text }) => {
return <Text>{text}</Text>;
});

Animated库中使用nativeDriver

React Native中有很多方法可以写动画,最常用的方法就是使用Animated库

Animated

Animated会在动画执行之前,通过nativeDriver把动画发送到原生bridge中,这有助于动画独立于被阻塞的JavaScript线程执行,动画会执行比较流畅而不会丢帧

通过设置useNativeDriver的值为true,可以在Animated库中使用nativeDriver。下面的例子就是在ScrollView组件的onScroll事件中使用useNativeDriver

1
2
3
4
5
6
7
8
9
10
<ScrollView
showsVerticalScrollIndicator={false}
scrollEventThrottle={1}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: animatedValue } } }],
{ useNativeDriver: true }
)}
>
// 组件的内容
</ScrollView>

使用Flipper进行调试

React Native 0.62.0版本介绍了一个新的调试工具Flipper。 这是一个给iOS、安卓和React Native使用的平台 。它直接集成在原生代码中,并且在React Native中开箱即用。

使用Flipper调试app不需要远程调试。需要一个本地连接的Metro实例来与React Native应用进行交互。它可通过React DevTools来检查组件树并检查React组件的state和属性。

它使用原生插件生态系统来调试iOS和Android应用程序。这些插件可用于设备日志、崩溃报告、检查网络请求、检查应用程序的本地数据库、检查缓存的图像等。

使用Hermes

Hermes是一个专为移动端应用优化的开源javascript引擎。React Native 0.60.4版本之后,Hermes在安卓也可用了。这有利于减少app的下载体积(安卓APK)、降低内存消耗和降低APP的可交互时间

在安卓APP中开启Hermes引擎,需要打开build.gradle并且修改如下:

1
def enableHermes = project.ext.react.get("enableHermes", true);

自React Native 0.64-rc.0版本后,Hermes也能用于iOS平台。需要打开Podfile并且修改如下:

1
use_react_native!(:path => config[:reactNativePath], :hermes_enabled => true

不要在源代码中保留console表达式

在Javascript应用包括React Native应用中,用console.log调试是最常用的调试方法之一。然而,在构建React Native应用时,将console语句留在源代码中可能对JavaScript线程造成一些瓶颈。

一个解决方法就是使用babel-plugin-transform-remove-console删除掉console语句。在终端通过下面的方法安装

1
yarn add babel-plugin-transform-remove-console

然后修改 .babelrc文件如下来删除所有的console语句

1
2
3
4
5
6
7
{
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}

不要使用Scrollview渲染一个大列表数据

有一些方法可以在React Native中使用滚动列表。其中两种最常用的方式就是使用ScrollViewFlatList组件

ScrollView用起来很简单,通常用于使用JavaScript的map()函数遍历一个数组。 例如:

1
2
3
4
5
<ScrollView>
{items.map(item => {
return <Item key={item.id.toString()} />;
})}
</ScrollView>

ScrollView会一次性渲染所有的子组件,在需要渲染的子组件数量不多的时候会比较好用。但在处理大量的数据的时候会影响到APP的性能。

为了解决渲染大量数据的情况,React Native提供了一个FlatList组件。这个组件能够懒加载子组件列表,这样APP就不会消耗大量的内存

例如:

1
2
3
4
5
6
<FlatList
data={elements}
keyExtractor={item => `${items.id}`}
renderItem={({ item }) => <Item key={item.id.toString()} />}
/>

结论

React Native是一个用于构建跨平台应用的开源框架。它以JavaScript为核心,并调用原生组件来构建移动端界面和功能。它会是一个高性能框架只要注意考虑到性能

  • Post title:React Native性能优化:应该做和不应该做的
  • Post author:flytam
  • Create time:2021-03-03 19:37:57
  • Post link:https://blog.flytam.vip/React Native性能优化:应该做和不应该做的.html
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.