减少Bundle包的大小
关于如何减少Bundle包的大小,目前主流的方法是拆分Bundle包,把框架代码和业务代码单独出来,框架代码非常大,因此要分离出来单独前置加载,而业务代码则变成很小的JS代码单独发布,下面提供一些前人的经验链接
那么进入App后的性能点又在哪里呢?还是回到Bridge
首先,在苹果和谷歌两位大佬的光环下,native代码在设备上的运行速度毋容置疑,而JS作为脚本语言,本来就是以快著称,也就是说两边的独立运行都很快,如此看来,性能瓶颈只会出现在两端的通信上,但两边其实不是直接通信的,而是通过Bridge做中间人,查找、调用模块、接口等操作逻辑,会产生到能让UI层明显可感知的卡顿,那么性能控制就变成了如何尽量减少Bridge所需要的逻辑。
- UI事件响应
- 这块内容都发生在Native端,以事件形式传递到JS端,只是一个触发器,不会有过度性能问题
- UI更新
- JS是决定显示什么界面,如何样式化页面的,一般都是由JS端发起UI更新,同时向native端同步大量的数据和UI结构,这类更新会经常出现性能问题,特别是界面复杂、数据变动量大、动画复杂、变动频率高的情况
- UI事件响应+UI更新
- 如果UI更新改动不大,那么问题不大 如果UI事件触发了UI更新,同时逻辑复杂、耗时比较长,JS端和Native端的数据同步可能会出现时间差,由此会引发性能问题
总结起来,核心的RN性能优化点就比较清晰明朗了
- 首屏渲染优化:处理JS Bundle包大小、文件压缩、缓存
- 提高组件响应速度:
- setNativeProps直接在底层更新Native组件属性(其实没有解决JS端与Native端的数据同步问题)
- 立即执行更新回调(requestAnimationFrame)
- 动画优化
- 通过使用Annimated类库,一次性把更新发送到Native端,由Native端自己负责更新
- 把一些耗时操作放到动画与UI更新之后执行(InteractionManager)
图片加载
1:这种场景往往是小尺寸 Image 容器加载了特别大的图片,比如说 100x100 的容器加载 1000x1000 的图片那么这种问题怎么解决呢?Image 有个 resizeMethod 属性,就是解决 Android 图片内存暴涨的问题。当图片实际尺寸和容器样式尺寸不一致时,决定以怎样的策略来调整图片的尺寸。
顺便提一下,Android 图片加载的时候,还会有一个 easy-in 的 300ms 加载动画效果,看上去会觉得图片加载变慢了,我们可以通过设置 fadeDuration 属性为 0,来关闭这个加载动画。
2:优先使用 32 位色彩深度的图片
为啥推荐使用 32 bit 图片呢?直接原因有 2 个:
- Android 推荐使用 ARGB_8888 格式的图片,因为这种图片显示效果更好
- iOS GPU 只支持加载 32 bit 的图片。如果是其他格式的(比如说 24 bit 的 jpg),会先在 CPU 里转为 32 bit,再传给 GPU
3:Image 和 ImageView 长宽保持一致
前面举了一个 100x100 的 ImageView 加载 1000x1000 Image 导致 Android 内存 OOM 的问题,我们提出了设置 resizeMethod={‘resize’} 的方法来缩减图片在内存中的体积。其实这是一种无奈之举,如果可以控制加载图片的大小,我们应该保持 Image 和 ImageView 长宽一致。
首先我们看看长宽不一致会引起的问题:
- Image 小于 ImageView:图片不清晰,表情包电子包浆质感
- Image 大于 ImageView:浪费内存,有可能会引起 OOM
- 尺寸不一致会带来抗锯齿计算,增加了图形处理负担
延时加载模块或文件
import React, { Component } from 'react';
import { Text } from 'react-native';
// You may want to log at the file level to verify when this is happening
console.log('VeryExpensive component loaded');
export default class VeryExpensive extends Component {
// lots and lots of code
render() {
return <Text>Very Expensive Component</Text>;
}
}
import React, { Component } from 'react';
import { TouchableOpacity, View, Text } from 'react-native';
// 先把这个组件赋值为null
let VeryExpensive = null;
export default class Optimized extends Component {
state = { needsExpensive: false };
didPress = () => {
if (VeryExpensive == null) {
// 真正需要这个组件的时候才加载
VeryExpensive = require('./VeryExpensive').default;
}
this.setState(() => ({
needsExpensive: true,
}));
};
render() {
return (
<View style={{ marginTop: 20 }}>
<TouchableOpacity onPress={this.didPress}>
<Text>Load</Text>
</TouchableOpacity>
// 根据需要判断是否渲染该组件
{this.state.needsExpensive ? <VeryExpensive /> : null}
</View>
);
}
}