React Native 原理浅析
0.简介
先说下了解这个有啥好处吧
- 有利于使用原生平台提供的更多的功能和API,或者接入第三方的库和服务,比如接入阿里的服务。
- 知道性能开销在哪,能针对的优化你的代码提高性能。
- debug,知道的越多当然更容易知道问题为什么出现了
- 更好的回答面试官的问题 😂😂
然后开始吧,RN的架构分为三块,js的部分,原生Native的部分,和连接这两者的桥Bridge。
以下为架构示意图
1. JavaScript线程
这是一个单独的线程,用来运行JavaScript代码,包括React组件的渲染逻辑,业务逻辑,事件处理等。JavaScript线程使用JavaScript引擎来执行代码,比如Hermes或者JSCore。
1.1 JavaScript引擎
JavaScript引擎是一个负责解析和执行JavaScript代码的模块。不同的JavaScript引擎有不同的性能和特性,比如Chrome用的是V8,Firefox用的是SpiderMonkey,Safari用的是JSCore。
而React Native则有两种选择,JSCore和Hermes。
JSCore兼容性更好,Hermes则性能更好更小(RN出品的)。
React Native默认使用JSCore,但是你也可以自己配置下,改成Hermes,
如果想深入了解可以看这两篇文章
1. React Native Memory profiling (JSC vs V8 vs Hermes)
2. 使用新的 Hermes 引擎 - react native
1.2 JavaScript代码
最熟悉的部分了,就你项目里的js代码了,包括React组件的渲染,业务逻辑,事件处理等。如果你已经做过RN的开发,那你会发现你可以用js的库,比如Typescript / Redux,当然了,也不是全部都能用,有一些不适配RN。
JS可以通过Bridge调用Native的方法函数,也能通过Bridge接收Native的消息。
2. Native线程
这个线程用来运行原生平台的代码,包括UI渲染,动画等。Native线程使用原生平台的语言和技术来执行代码,iOS是Objective-C或者Swift,而Android则是Java或者Kotlin。
2.1 原生平台
原生平台是指运行React Native应用的操作系统和设备,比如iOS平台和iPhone设备,Android平台和华为小米设备等。原生平台提供了一些基础的功能和API,比如视图(View),文本(Text),图像(Image),按钮(Button),滚动视图(ScrollView),列表视图(ListView)等。这些功能和API可以被React Native封装成对应的React组件,并通过Bridge暴露给JavaScript线程。
所以,我们在写RN时import RN的 View Text,在IOS渲染用的就是IOS的组件,在安卓机渲染时用的就是安卓的组件。
原生平台也提供了一些高级的功能和API,比如相机(Camera),定位(Geolocation),剪贴板(Clipboard)等。这些功能和API被React Native封装成Native模块,并通过Bridge注册和导出给JavaScript线程。
2.2 原生代码
原生代码在Native线程中执行,并通过Bridge与JS线程通信。原生代码可以接收JS线程发送过来的数据和命令,并执行相应的操作。也可以写一些原生的代码,在需要的时候通过Bridge传递给JS线程,说的有点抽象,懂的自然懂。
3. Bridge
这是一个中间层,用来连接JavaScript线程和Native线程,实现数据和命令的传递和处理。Bridge使用序列化和反序列化的方式来编码和解码消息,并使用消息队列来缓存和发送消息。
Bridge是异步多,不会阻塞任何一方的执行,但消息多了也会影响性能。
同时也是一个单向的,它只能从一方向另一方发送消息,而不能同时进行双向通信。所以需要一种协议来规范消息的格式和含义,以及一种机制来实现回调和错误处理。
3.1 消息格式
Bridge传递消息用的是JSON。
这个消息JSON长这样。
{
"module": "Camera",
"method": "takePicture",
"args": [true, 0.8],
"callbackId": 1
}
解释一下
这个消息表示JavaScript线程调用了Camera模块的takePicture方法,并传递了两个参数。
同时,这个消息指定了一个回调ID为1,表示JavaScript线程期望Native线程在执行完毕后返回结果或者错误信息。
3.2 消息队列
Bridge使用消息队列来缓存和发送消息,这是一种先进先出(FIFO)的数据结构,可以保证消息的顺序和完整性。
Bridge维护了两个消息队列:
一个用来存储JavaScript线程发送给Native线程的消息。
另一个用来存储Native线程发送给JavaScript线程的消息。
当JavaScript线程或者Native线程有新的消息要发送时,它会将消息添加到对应的消息队列中,并通知对方有新的消息可用。当对方收到通知后,它会从消息队列中取出所有的消息,并依次处理。
3.3 消息处理
Bridge使用事件驱动的方式来处理消息,这是一种基于回调函数的编程模式,它可以实现异步和非阻塞的通信。
Bridge为每个Native模块注册了一个回调函数,并将其映射到一个模块ID。
JavaScript线程调用一个Native模块时,它会通过Bridge发送一个包含模块ID,方法名,参数等信息的消息,并指定一个回调ID。
Native线程收到这个消息后,它会根据模块ID找到对应的回调函数,并执行该函数。
函数执行完毕后,它会通过Bridge发送一个包含回调ID,结果或者错误信息等信息的消息。
JavaScript线程收到这个消息后,它会根据回调ID找到对应的回调函数,并执行该函数。
4. Native模块
这是一些封装了原生平台功能的模块,可以被JavaScript线程调用,比如Camera,Geolocation,Clipboard等。
Native模块使用原生的语言来实现,并通过Bridge注册和导出给JavaScript线程。
JavaScript线程可以通过Bridge向Native模块发送请求,并接收返回值或者错误信息。
Native模块可以让JavaScript线程使用原生平台提供的功能和API。
4.1 Native模块的实现
为了便于Bridge识别和调用,Native模块需要遵循一定的规范和接口。
不同的原生平台有不同的实现方式,但是基本思路都差不多。
举个栗子🌰
// iOS平台
#import <React/RCTBridgeModule.h>
@interface RCTCameraModule : NSObject <RCTBridgeModule>
@end
@implementation RCTCameraModule
// 注册模块名
RCT_EXPORT_MODULE(Camera);
// 注册方法名
RCT_EXPORT_METHOD(takePicture:(BOOL)flash quality:(float)quality callback:(RCTResponseSenderBlock)callback)
{
// 实现方法逻辑
UIImage *image = ...;
if (image) {
// 返回结果
callback(@[[NSNull null], image]);
} else {
// 返回错误
callback(@[@"Camera error", [NSNull null]]);
}
}
@end
// Android
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class CameraModule extends ReactContextBaseJavaModule {
public CameraModule(ReactApplicationContext reactContext) {
super(reactContext);
}
// 注册模块名
@Override
public String getName() {
return "Camera";
}
// 注册方法名
@ReactMethod
public void takePicture(boolean flash, float quality, Callback callback) {
// 实现方法逻辑
Bitmap image = ...;
if (image != null) {
// 返回结果
callback.invoke(null, image);
} else {
// 返回错误
callback.invoke("Camera error", null);
}
}
}
4.2 Native模块的调用
Native模块的调用要用RN的API。
// JavaScript
import { NativeModules } from 'react-native';
// 获取Native模块对象
const Camera = NativeModules.Camera;
// 调用Native模块方法,并传递参数和回调函数
Camera.takePicture(true, 0.8, (error, image) => {
if (error) {
// 处理错误
console.error(error);
} else {
// 处理结果
console.log(image);
}
});
以上就是全部内容了,欢迎点赞转发收藏,有问题或勘误请留言,感谢。