React Native iOS混合開發實戰教程

在作RN開發的時候一般離不了JS 和Native之間的通訊,好比:初始化RN時Native向JS傳遞數據,JS調用Native的相冊選擇圖片,JS調用Native的模塊進行一些複雜的計算,Native將一些數據(GPS信息,陀螺儀,傳感器等)主動傳遞給JS等。html

在這篇文章中我將向你們介紹在RN中JS和Native之間通訊的幾種方式以及其原理和使用技巧;react

接下來我將分場景來介紹JS 和Native之間的通訊。android

幾種通訊場景:git

  • 初始化RN時Native向JS傳遞數據;
  • Native發送數據給JS;
  • JS發送數據給Native;
  • JS發送數據給Native,而後Native回傳數據給JS;

React-Native-JS-Native-Communication

1. 初始化RN時Native向JS傳遞數據

init-data-to-js

在RN的API中提供了Native在初始化JS頁面時傳遞數據給JS的方式,這種傳遞數據的方式比下文中所講的其餘幾種傳遞數據的方式發生的時機都早。github

由於不多有資料介紹這種方式,因此可能有不少朋友還不知道這種方式,不過沒關係,接下來我就向你們介紹如何使用這種方式來傳遞數據給JS。react-native

概念

RN容許咱們在初始化JS頁面時向頂級的JS 組件傳遞props數據,頂級組件能夠經過this.props來獲取這些數據。bash

iOSmarkdown

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName //這個"App1"名字必定要和咱們在index.js中註冊的名字保持一致
                         initialProperties:@{@"params":self.paramsInit}//RN初始化時傳遞給JS的初始化數據
                             launchOptions: nil];
複製代碼

接下來,咱們先來看一下如何在iOS上來傳遞這些初始化數據。less

iOS向RN傳遞初始化數據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}/>;
    }
    ...
}
複製代碼

2. Native到JS的通訊(Native發送數據給JS)

init-data-to-js

在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
複製代碼

在上述方法中,咱們經過RCTEventEmittersendEventWithName方法將名爲eventName的數據params傳遞給了JS。

提示:在DataToJSPresenter中咱們實現了(NSArray<NSString *> *)supportedEvents方法,該方法用於指定可以發送給JS的事件名,因此發送給JS的eventName必定要在這個方法中進行配置不然沒法發送。

實現Native到JS的通訊所須要的步驟

接下來咱們來總結一下,要實現Native到JS的通訊所須要的步驟:

  • 首先要實現RCTEventEmitter <RCTBridgeModule>
  • 經過RCTEventEmittersendEventWithName方法將數據傳遞給JS;

經過上述步驟,咱們就能夠將數據從Native發動到JS,那麼如何在JS中來獲取這些數據呢?

在JS中獲取Native經過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>
        );
    }
}
複製代碼

在上述代碼中,咱們經過NativeEventEmitteraddListener添加了一個監聽器,該監聽器會監聽Native發過來的名爲testData的數據,這個testData要和上文中所講的eventName要保持一致:

[self sendEventWithName:eventName body:params];
複製代碼

coding.imooc.com/lesson/89.h… 另外,記得在JS組件卸載的時候及時移除監聽器。

以上就是在iOS中實現Native到JS通訊的原理及方式,接下來咱們來看一下實現JS到Native之間通訊的原理及方式。

3. JS到Native的通訊(JS發送數據給Native)

init-data-to-js

咱們所封裝的NativeModule就是給JS用的,它是一個JS到Native通訊的橋樑,JS能夠經過它來實現向Native的通訊(傳遞數據,打開Native頁面等),接下來我就來藉助NativeModule來實現JS到Native的通訊。

關於如何實現NativeModule你們能夠學習參考React Native原生模的封裝

首先實現JSBridgeModule

首先咱們須要實現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
複製代碼

代碼解析

  1. JSBridgeModule中,咱們實現了一個RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params)方法,該方法主要用於暴露給JS調用,來傳遞數據params給Native;
  2. 當收到數據後,經過NSNotificationCenter以通知的形式將數據發送出去;

JS調用JSBridgeModule發送數據給Native

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;

JSBridge.sendMessage({text: this.text})
複製代碼

經過上述代碼我就能夠將一個Map類型的數據{text: this.text}傳遞給Native。

4. JS發送數據給Native,而後Native回傳數據給JS

init-data-to-js

經過上文所講的JS到Native的通訊(JS發送數據給Native),咱們已經實現了JS到Native的通訊,當時咱們藉助的是JSBridgeModule,其實它的功能還不侷限於此,藉助它咱們還能夠實現Native到JS的數據回傳。

在Native的實現

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,在這裏咱們用的是RCTPromiseResolveBlockRCTPromiseRejectBlock兩種類型的回調,分別表明成功和失敗。

在JS中的實現

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方法將兩個整數num1num2傳遞給了Native,而後經過then來監聽回傳結果,整個過程採用了Promise的鏈式調用方式。

參考

相關文章
相關標籤/搜索