在作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
的鏈式調用方式。