在作RN开发的时候一般离不了JS 和Native之间的通讯,好比:初始化RN时Native向JS传递数据,JS调用Native的相册选择图片,JS调用Native的模块进行一些复杂的计算,Native将一些数据(GPS信息,陀螺仪,传感器等)主动传递给JS等。html
在这篇文章中我将向你们介绍在RN中JS和Native之间通讯的几种方式以及其原理和使用技巧;react
接下来我将分场景来介绍JS 和Native之间的通讯。android
几种通讯场景:git
在RN的API中提供了Native在初始化JS页面时传递数据给JS的方式,这种传递数据的方式比下文中所讲的其余几种传递数据的方式发生的时机都早。github
由于不多有资料介绍这种方式,因此可能有不少朋友还不知道这种方式,不过没关系,接下来我就向你们介绍如何使用这种方式来传递数据给JS。react-native
RN容许咱们在初始化JS页面时向顶级的JS 组件
传递props
数据,顶级组件能够经过this.props
来获取这些数据。bash
iOS
markdown
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation moduleName: self.moduleName //这个"App1"名字必定要和咱们在index.js中注册的名字保持一致 initialProperties:@{@"params":self.paramsInit}//RN初始化时传递给JS的初始化数据 launchOptions: nil]; 复制代码
接下来,咱们先来看一下如何在iOS
上来传递这些初始化数据。less
initialProperties
RN的RCTRootView提供了initWithBundleURL
方法来渲染一个JS 组件,在这个方法中提供了一个用于传递给这个JS 组件的初始化数据的参数。oop
方法原型:
- (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties launchOptions:(NSDictionary *)launchOptions
复制代码
jsCodeLocation
:要渲染的RN的JS页面的路径;moduleName
:要加载的JS模块名;initialProperties
:要传递给顶级JS组件
的初始化数据;launchOptions
:主要在AppDelegate加载JS Bundle时使用,这里传nil就行;经过上述方法的第三个参数就能够将一个NSDictionary
类型的数据传递给顶级JS组件
。
示例代码:
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation moduleName: self.moduleName initialProperties:@{@"params":@"这是传递给顶级JS组件的数据"}//RN初始化时传递给JS的初始化数据 launchOptions: nil]; 复制代码
在上述代码中,咱们将一个名为params
的数据这是传递给顶级JS组件的数据
传递给了顶级的JS 组件
,而后在顶级的JS 组件
中就能够经过以下方法来获取这个数据了:
render() { const {params}=this.props; return ( <View style={styles.container}> <Text style={styles.data}>来自Native初始化数据:{params}</Text> </View> ); } 复制代码
另外,若是要在非顶级页面如CommonPage
中使用这个初始化数据,则能够经过以下方式将数据传递到CommonPage
页面:
export default class App extends Component<Props> { ... render() { return <CommonPage {...this.props}/>; } ... } 复制代码
在RN的iOS SDK中提供了一个RCTEventEmitter
接口,咱们能够经过该接口实现Native到JS的通讯,也就是Native将数据传递给JS。
- (void)sendEventWithName:(NSString *)name body:(id)body;
复制代码
因此只要咱们得到RCTEventEmitter
的实例就能够借助它将数据传递给JS。为了得到RCTEventEmitter
的实例咱们能够经过继承RCTEventEmitter <RCTBridgeModule>
的方式来实现:
DataToJSPresenter.h
/** * React Native JS Native通讯 * Author: CrazyCodeBoy * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702 * GitHub:https://github.com/crazycodeboy * Email:crazycodeboy@gmail.com */ #import <React/RCTBridgeModule.h> #import <React/RCTEventEmitter.h> @interface DataToJSPresenter : RCTEventEmitter <RCTBridgeModule> @end 复制代码
DataToJSPresenter.m
/** * React Native JS Native通讯 * Author: CrazyCodeBoy * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702 * GitHub:https://github.com/crazycodeboy * Email:crazycodeboy@gmail.com */ #import "DataToJSPresenter.h" @implementation DataToJSPresenter RCT_EXPORT_MODULE(); - (NSArray<NSString *> *)supportedEvents { return @[@"testData"]; } - (instancetype)init { if (self = [super init]) {//在module初始化的时候注册fireData广播 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fireData:) name:@"fireData" object:nil]; } return self; } - (void)fireData:(NSNotification *)notification{//发送数据给RN NSString *eventName = notification.object[@"name"]; NSDictionary *params = notification.object[@"params"]; [self sendEventWithName:eventName body:params]; } @end 复制代码
在上述方法中,咱们经过RCTEventEmitter
的sendEventWithName
方法将名为eventName
的数据params
传递给了JS。
提示:在
DataToJSPresenter
中咱们实现了(NSArray<NSString *> *)supportedEvents
方法,该方法用于指定可以发送给JS的事件名,因此发送给JS的eventName
必定要在这个方法中进行配置不然没法发送。
接下来咱们来总结一下,要实现Native到JS的通讯所须要的步骤:
RCTEventEmitter <RCTBridgeModule>
;RCTEventEmitter
的sendEventWithName
方法将数据传递给JS;经过上述步骤,咱们就能够将数据从Native发动到JS,那么如何在JS中来获取这些数据呢?
RCTEventEmitter
传过来的数据在JS中能够经过NativeEventEmitter
来获取Native经过RCTEventEmitter
传过来的数据,具体方法以下:
import {NativeEventEmitter} from 'react-native'; export default class CommonPage extends Component<Props> { constructor(props) { super(props); this.state = { data: "", result: null } } componentWillMount() { this.dataToJSPresenter = new NativeEventEmitter(NativeModules.DataToJSPresenter); this.dataToJSPresenter.addListener('testData', (e) => {// for iOS this.setState({ data: e.data }) }) } componentWillUnmount() { if (this.dataToJSPresenter){ this.dataToJSPresenter.removeListener('testData'); } } render() { return ( <View style={styles.container}> <Text style={styles.data}>收到Native的数据:{this.state.data}</Text> </View> ); } } 复制代码
在上述代码中,咱们经过NativeEventEmitter
的addListener
添加了一个监听器,该监听器会监听Native发过来的名为testData
的数据,这个testData
要和上文中所讲的eventName
要保持一致:
[self sendEventWithName:eventName body:params];
复制代码
coding.imooc.com/lesson/89.h… 另外,记得在JS组件卸载的时候及时移除监听器。
以上就是在iOS中实现Native到JS通讯的原理及方式,接下来咱们来看一下实现JS到Native之间通讯的原理及方式。
咱们所封装的NativeModule就是给JS用的,它是一个JS到Native通讯的桥梁,JS能够经过它来实现向Native的通讯(传递数据,打开Native页面等),接下来我就来借助NativeModule来实现JS到Native的通讯。
关于如何实现NativeModule你们能够学习参考React Native原生模的封装
首先咱们须要实现RCTBridgeModule
:
JSBridgeModule.h
/** * React Native JS Native通讯 * Author: CrazyCodeBoy * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702 * GitHub:https://github.com/crazycodeboy * Email:crazycodeboy@gmail.com */ #import <React/RCTBridgeModule.h> @interface JSBridgeModule : NSObject <RCTBridgeModule> @end 复制代码
JSBridgeModule.m
/** * React Native JS Native通讯 * Author: CrazyCodeBoy * 视频教程:https://coding.imooc.com/lesson/89.html#mid=2702 * GitHub:https://github.com/crazycodeboy * Email:crazycodeboy@gmail.com */ #import "JSBridgeModule.h" @implementation JSBridgeModule RCT_EXPORT_MODULE(); - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue();//让RN在主线程回调这些方法 } RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params){//接受RN发过来的消息 [[NSNotificationCenter defaultCenter] postNotificationName:@"sendMessage" object:params]; } @end 复制代码
代码解析
JSBridgeModule
中,咱们实现了一个RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params)
方法,该方法主要用于暴露给JS调用,来传递数据params
给Native;NSNotificationCenter
以通知的形式将数据发送出去;import {NativeModules} from 'react-native'; const JSBridge = NativeModules.JSBridgeModule; JSBridge.sendMessage({text: this.text}) 复制代码
经过上述代码我就能够将一个Map类型的数据{text: this.text}
传递给Native。
经过上文所讲的JS到Native的通讯(JS发送数据给Native)
,咱们已经实现了JS到Native的通讯,当时咱们借助的是JSBridgeModule
,其实它的功能还不局限于此,借助它咱们还能够实现Native到JS的数据回传。
在JSBridgeModule
中添加以下方法:
RCT_EXPORT_METHOD(doAdd:(NSInteger )num1 num2:(NSInteger )num2 resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSInteger result=num1+num2; resolve([NSString stringWithFormat:@"%ld",(long)result]);//回调JS } 复制代码
上述代码暴露给了JS一个简单的两个整数之间的加法运算,并将运算结果回传给JS,在这里咱们用的是RCTPromiseResolveBlock
与RCTPromiseRejectBlock
两种类型的回调,分别表明成功和失败。
import {NativeModules} from 'react-native'; const JSBridge = NativeModules.JSBridgeModule; JSBridge.doAdd(parseInt(this.num1), parseInt(this.num2)).then(e => { this.setState({ result: e }) }) 复制代码
在JS中咱们经过JSBridge.doAdd
方法将两个整数num1
与num2
传递给了Native,而后经过then
来监听回传结果,整个过程采用了Promise
的链式调用方式。