ReactNative通信机制和渲染流程

前言

React Native与传统的HybirdApp最大区别就是抛开WebView,使用JSC+原生组件的方式进行渲染,那么整个App启动/渲染流程又是怎样的呢?

一、整体框架

RN 这套框架让 JS开发者可以大部分使用JS代码就可以构建一个跨平台APP;



React 与 React native 的原理是相同的,都是由 javascript 实现的虚拟DOM 来驱动界面 View 的渲染,只不过 React.js 驱动 HTML DOM 的渲染, RN 是驱动 ios/android 原生组件的渲染

二、前端页面开发及渲染

1、js入口

import { AppRegistry } from "react-native";
import App from "./src/index";

AppRegistry.registerComponent("Wubacst", () => App);

把当前APP的对象注册到AppRegistry组件中, AppRegistry组件是js module

2、RN AppRegistry

  • AppRegistry 是运行所有 React Native 应用程序的 JS 入口点
  • 应用程序跟组件需要通过 AppRegistry.registerComponent 来注册它们自身
  • 然后本地系统就可以加载应用程序的包,再然后当 AppRegistry.runApplication准备就绪后就可以真正的运行该应用程序了
  • AppRegistry 在 require 序列里是 required,确保在其他模块被需求之前 JS 执行环境已经被 required

AppRegistry常用方法

//static静态方法,用来注册配置信息
static **registerConfig**(config: Array) 

//static静态方法,注册组件
static **registerComponent**(appKey: string, getComponentFunc: Function) 

//static静态方法,注册线程
static **registerRunnable**(appKey: string, func: Function) 

//static静态方法,进行运行应用 
static **runApplication**(appKey: string, appParameters: any)
runApplication: function runApplication(appKey, appParameters) {
      var msg = 'Running application "' + appKey + '" with appParams: ' + JSON.stringify(appParameters) + '. ' + '__DEV__ === ' + String(__DEV__) + ', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') + ', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
      infoLog(msg);
      BugReporting.addSource('AppRegistry.runApplication' + runCount++, function () {
        return msg;
      });
      invariant(runnables[appKey] && runnables[appKey].run, 'Application ' + appKey + ' has not been registered.\n\n' + "Hint: This error often happens when you're running the packager " + '(local dev server) from a wrong folder. For example you have ' + 'multiple apps and the packager is still running for the app you ' + 'were working on before.\nIf this is the case, simply kill the old ' + 'packager instance (e.g. close the packager terminal window) ' + 'and start the packager in the correct app folder (e.g. cd into app ' + "folder and run 'npm start').\n\n" + 'This error can also happen due to a require() error during ' + 'initialization or failure to call AppReg
      istry.registerComponent.\n\n');
      SceneTracker.setActiveScene({
        name: appKey
      });
      runnables[appKey].run(appParameters);
    }


3、ReactDOM.render

ReactDOM.render是React的最基本方法用于将模板转为HTML语言,并插入指定的DOM节点。

ReactDOM.render(template,targetDOM),该方法接收两个参数:
  • 第一个是创建的模板
  • 第二个参数是插入该模板的目标位置
ReactDOM.render(
  ,
  document.getElementById('app')
);


4、RN组件的生命周期

像 Android和iOS 开发一样,React Native(RN) 中的组件也有生命周期(Lifecycle);
当应用启动,React Native在内存中维护着一个虚拟DOM,组件的生命周期就是指组件初始化并挂载到虚拟DOM为起始,到组件从虚拟DOM卸载为终结。生命周期的方法就是组件在虚拟DOM中不同状态的描述。



组件的生命周期分为三个阶段:

  • 挂载(mounting
  • 更新(updating)
  • 卸载(Unmounting

组件挂载

挂载指的是组件的实例被创建并插入到DOM中,挂载会调用如下方法。
挂载阶段调用方法:

constructor(props) {
  super(props);
  this.state = {
    text: '构造方法'
  };
}

constructor是RN组件的构造方法,它在RN组件被加载前先被调用。当我们的组件继承自React.Component时,需要在构造方法中最先调用super(props)。如果不需要初始化state,则不需要实现构造方法。

componentWillMount()


componentWillMount方法在挂载前被立即调用。它在render方法前被执行,因此,在componentWillMount方法中设置state并不会导致重新被渲染。它只会被执行一次

render()


渲染,render方法中不应该修改组件的props和state;render方法在更新阶段也会被调用,前提是shouldComponentUpdate方法返回true。

componentDidMount()


componentDidMount方法在组件被挂载后立即调用,在render方法后被执行

可以在这个方法中获取其中的元素或者子组件,需要注意的是,子组件的componentDidMount方法会在父组件的componentDidMount方法之前调用

如果需要从网络加载数据显示到界面上,最好在这里进行网络请求。在componentDidMount方法中设置state将会被重新渲染。

组件更新

改变props或者state时可以导致更新,当一个组件被重新渲染时,会调用如下方法。

componentWillReceiveProps(nextProps)


componentWillReceiveProps方法会在挂载的组件接收到新的props时被调用,它接收一个Object类型参数nextProps,表示新的props。通常在这个方法中接收新的props值,并根据props的变化,通过调用 this.setState() 来更新组件state,this.setState()不会触发 render方法的调用。

在挂载的过程中,初始的props并不会触发调用componentWillReceiveProps方法,这个方法只会在组件中的props更新时被调用,另外,调用this.setState()也不会触发调用componentWillReceiveProps方法。

shouldComponentUpdate(nextProps, nextState)


当组件接收到新的props和state时,shouldComponentUpdate方法被调用,它接收两个Object参数,nextProps是新的props,nextState是新的state。

shouldComponentUpdate方法默认返回true,用来保证数据变化时,组件能够重新渲染。你也可以重载这个方法,通过检查变化前后props和state,来决定组件是否需要重新渲染。如果返回false,则组件不会被重新渲染,也不会调用本方法后面的componentWillUpdate和componentDidUpdate方法。

componentWillUpdate(nextProps, nextState)


如果组件props或者state改变,并且此前的shouldComponentUpdate方法返回为 true,则会调用该方法。componentWillUpdate方法会在组件重新渲染前被调用,因此,可以在这个方法中为重新渲染做一些准备工作。需要注意的是,在这个方法中,不能使用 this.setState 来更改state,如果想要根据props来更改state,需要在componentWillReceiveProps方法中去实现,而不是在这里

componentDidUpdate(prevProps, prevState)


组件重新渲染完成后会调用componentDidUpdate方法。两个参数分别是渲染前的props和渲染前的state。这个方法也适合写网络请求,比如可以将当前的props和prevProps进行对比,发生变化则请求网络。

组件卸载

卸载就是从DOM中删除组件,会调用如下方法

componentWillUnmount()


componentWillUnmount方法在组件卸载和销毁之前被立即调用。可以在这个方法中执行必要的清理工作,比如,关掉计时器、取消网络请求、清除组件装载中创建的DOM元素等等。

三、React Native如何把React转化为元素的API

React Native会在一开始生成OC模块表,然后把这个模块表传入JS中,JS参照模块表,就能间接调用OC的代码。

相当于买了一个机器人(OC),对应一份说明书(模块表),用户(JS)参照说明书去执行机器人的操作

四、React Native如何如何做到JS和OC交互

iOS原生API有个[JavaScriptCore](https://www.jianshu.com/p/bd7dfd8c9917)框架,通过它就能实现JS和OC交互

  1. 首先写好前端jsx代码
  2. 把jsx代码解析成JavaScript代码
  3. OC读取JS文件
  4. 把JavaScript读取出来,利用JavaScriptCore执行
  5. javaScript代码返回一个数组,数组中会描述OC对象,OC对象的属性,OC对象所需要执行的方法,这样就能让这个对象设置属性,并且调用方法


五、RN启动流程(iOS)

流程图:


1、创建RCTRootView

设置窗口根控制器的View,把RN的View添加到窗口上显示

我们使用RCTRootView将React Natvie视图封装到原生组件中。RCTRootView是一个UIView容器,承载着React Native应用。同时它也提供了一个联通原生端和被托管端的接口

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                      moduleName:@"Wubacst"
                                     initialProperties:props];


import { AppRegistry } from "react-native";
import App from "./src/index";

AppRegistry.registerComponent("Wubacst", () => App);


创建RCTBridge

桥接对象,管理JS和OC交互,做中转左右

1、RCTBridgeModule
在React Native中,如果实现一个原生模块,需要实现RCTBridgeModule”协议

2、RCT_EXPORT_MODULE()
如果我们实现了RCTBridgeModule协议,我们的类需要包含RCT_EXPORT_MODULE()宏。这个宏也可以添加一个参数用来指定在Javascript中访问这个模块的名字。如果你不指定,默认就会使用这个Objective-C类的名字

3、RCT_EXPORT_METHOD()
与此同时我们需要声明RCT_EXPORT_METHOD()宏来实现要给Javascript导出的方法,否则React Native不会导出任何方法。

native方法注册示例:

@implementation LoadPageManager
RCT_EXPORT_MODULE()

// RN跳转Native //
RCT_EXPORT_METHOD(showCSTNativePage:(NSDictionary*)commonParames
                  disappearCallBack:(RCTResponseSenderBlock) disappearCallBack)
{
    NSLog(@"start load");
    NSString* pagePath = [commonParames objectForKey:@"pagePath"];
    NSDictionary* aParams = [commonParames objectForKey:@"params"];
    NSString* aPostMessageEvent = [commonParames objectForKey:@"postMessageEvent"];
    NSInteger isCloseParent = [(NSNumber*)[commonParames objectForKey:@"isDestoryBeforePage"] intValue];
    NSString* aAppearEvent = [commonParames objectForKey:@"appearEvent"];
    
    NSMutableDictionary* aTmpDic = [NSMutableDictionary dictionaryWithDictionary:commonParames];
    // 以下代码不要删除 //
    NSMutableDictionary* aDic;
    if (aParams){
        aDic = [NSMutableDictionary dictionaryWithDictionary:aParams];
    }

    if (aPostMessageEvent){
        [aDic setValue:aPostMessageEvent forKey:PostEvent];
    }
   
    if (aAppearEvent){
        [aDic setValue:aAppearEvent forKey:AppearEvent];
    }
    // 以下代码不要删除 //
    
    if (disappearCallBack){
        [aTmpDic setValue:disappearCallBack forKey:DisappearCallBack];
    }
    
    
    if (isCloseParent){
        [self RNBackAction];
    }
    // 转一下子为了和之前Hybrid 一致 //
    pagePath = [pagePath stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
    aDic = [self adpatToHybridParmers:[NSMutableDictionary dictionaryWithDictionary:aTmpDic]]; // 后期重做考虑去掉 //
    // 转一下子为了和之前Hybrid 一致 //
    
    dispatch_async(dispatch_get_main_queue(), ^{
       [[CHRDispatcher sharedDispatcher] invokeForPath:pagePath andParams: aDic];
    });
}


js调用示例:

import { NativeModules, Platform } from "react-native";
import checker from "../lib/checker";
let pageManager = NativeModules.LoadPageManager;
function nativeBridge(type, params, disappearCallBack) {
    pageManager[type](params, disappearCallBack);
}
//RN跳转native页面
function jumpNativePage(
    { pagePath, isDestoryBeforePage = 0, jumpParameter, appearEvent, postMessageEvent, ...other },
    disappearCallBack = (error, nativeData) => {} //回调函数
) {
    const error = checker(
        arguments,
        [
            {
                pagePath: "s|r",
                isDestoryBeforePage: "b",
                jumpParameter: "o",
                appearEvent: "s",
                postMessageEvent: "s",
                ...other
            },
            "f"
        ],
        "jumpNativePage"
    );
    // 线上环境报错不调起 Native 的 API
    if (error === "error") {
        return error;
    }
    let param = {
        pagePath,
        jumpParameter,
        appearEvent,
        postMessageEvent, //事件名称
        isDestoryBeforePage, //0:不关闭,1关闭
        ...other
    };
    nativeBridge("showCSTNativePage", param, disappearCallBack);
}


3、创建RCTBatchedBridge

批量桥接对象,JS和OC交互具体实现都在这个类中

4、执行[RCTBatchedBridge loadSource]

加载JS源码

5、执行[RCTBatchedBridge initModulesWithDispatchGroup

创建OC模块表

6、执行[RCTJSCExecutor injectJSONText]

往JS中插入OC模块表

7、执行完JS代码,回调OC,调用OC中的组件

8、完成UI渲染

六、RN UI控件渲染流程

1、RCTRootView runApplication:bridge

通知JS运行App

2、RCTBatchedBridge _processResponse:json error:error

处理执行完JS代码(runApplication)返回的相应,包含需要添加多少子控件的信息。

3、RCTBatchedBridge batchDidComplete

RCTUIManager调用处理完成的方法,就会开始去加载rootView的子控件。

4、RCTUIManager createView:viewName:rootTag:props

通过JS执行OC代码,让UI管理者创建子控件View

通过RCT_EXPORT_METHOD宏定义createView这个方法

RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
                  viewName:(NSString *)viewName
                  rootTag:(nonnull NSNumber *)rootTag
                  props:(NSDictionary *)props)


RCT_EXPORT_METHOD宏:会在JS中生成对应的OC方法,这样JS就能直接调用

注意每创建一个UIView,就会创建一个RCTShadowView,与UIView一一对应

RCTShadowView:保存对应UIView的布局和子控件,管理UIView的加载

5、[RCTUIManager _layoutAndMount]

布局RCTRootView和增加子控件

6、[RCTUIManager setChildren:reactTags:]

给RCTRootView对应的RCTRootShadowView设置子控件

注意:此方法也是JS调用OC方法
7、[RCTRootShadowView insertReactSubview:view atIndex:index++]

遍历子控件数组,给RCTRootShadowView插入所有子控件

8、[RCTShadowView processUpdatedProperties:parentProperties:]

处理保存在RCTShadowView中属性,就会去布局RCTShadowView对应UIView的所有子控件

9、[RCTView didUpdateReactSubviews]

给原生View添加子控件

10、完成UI渲染


七、RN通信机制

OC生成一张模块配置表,包含所有模块和模块里的方法,根据特定的标识宏(RCT_EXPORT_MODULE()),将可以暴露的方法暴露给JS。

1、OC-JS交互流程:

  1. js调用OC模块暴露出来的方法
  2. 把调用方法分解为ModuleName、MethodName、arguments,在丢给MessageQueue处理
  3. 把js的callback函数缓存在MessageQueue的一个成员变量里面,同时生成一个CallbackID来代表callback;在通过保存在MessageQueue的模块配置表把ModuleName、MethodName转成ModuleID、MethodID
  4. 把ModuleID、MethodID、CallbackID和其他参数传给OC(JavaScriptCore)
  5. OC接到消息,通过模块配置表拿到对于的模块和方法
  6. RCTModuleMethod对js传过来的参数进行处理
  7. OC模块方法执行完,执行block回调
  8. 调用第6步中RCTModuleMethod生成的block
  9. block带着CallbackID和block传过来的参数去掉用js里的MessageQueue方法invokeCallbackAndReturnFlushedQueue
  10. MessageQueue通过CallbackID找到相应的js的callback方法
  11. 调用callback方法,并把OC带过来的参数一起传过去完成回调
    React Native用iOS自带的JavaScriptCore作为JS的解析引擎,但并没有用到JavaScriptCore提供的一些可以让JS与OC互调的特性,而是自己实现了一套机制,这套机制可以通用于所有JS引擎上,在没有JavaScriptCore的情况下也可以用webview代替,实际上项目里就已经有了用webview作为解析引擎的实现,应该是用于兼容iOS7以下没有JavascriptCore的版本

通信过程

所谓的通信其实就是js和oc两者如何相互调用传参等
  • 程序一开始native会调用js的RCTDeviceEventEmitter.emit方法
  • 分别发送’appStateDidChange’ 和’networkStatusDidChange’两个事件
iOS端:
if (![connectionType isEqualToString:self->_connectionType] ||
      ![effectiveConnectionType isEqualToString:self->_effectiveConnectionType] ||
      ![status isEqualToString:self->_statusDeprecated]) {
    self->_connectionType = connectionType;
    self->_effectiveConnectionType = effectiveConnectionType;
    self->_statusDeprecated = status;
    [self sendEventWithName:@"networkStatusDidChange" body:@{@"connectionType": connectionType,
                                                             @"effectiveConnectionType": effectiveConnectionType,
                                                             @"network_info": status}];
  }
  
  if (![newState isEqualToString:_lastKnownState]) {
    _lastKnownState = newState;
    [self sendEventWithName:@"appStateDidChange"
                       body:@{@"app_state": _lastKnownState}];
  }
  //以上这些方法都是RN提供给iOS的在RCTAppState.m中


RN端:
if (type === 'change') {
          this._eventHandlers[type].set(handler, this.addListener('appStateDidChange', function (appStateData) {
            handler(appStateData.app_state);
          }));
        } else if (type === 'memoryWarning') {
          this._eventHandlers[type].set(handler, this.addListener('memoryWarning', handler));
        }


  • 接着调用js的AppRegistry.runApplication方法启动js应用
  • 然后js层就可以通过native提供的方法来 RCTUIManager.createView来创建视图了

2、RN端发消息到iOS的过程

首先在iOS里面新建一个类LoadPageManager
LoadPageManager会实现RCTBridgeModule协议
在LoadPageManager累的实现中添加宏定义:RCT_EXPORT_MODULE()
RCT_EXPORT_MODULE()如果你不传入参数,那么你在iOS中导出的模块名就是类名,你也可以插入参数作为自定义模块名

iOS端代码
@implementation LoadPageManager
//可以指定一个参数来访问这个模块,不指定就是这个类的名字(ExampleInterface)
RCT_EXPORT_MODULE()
@end


接下来就可以实现协议的代理方法了,协议方法的实现需要在RCT_EXPORT_METHOD这个宏里面;下面以一个页面跳转的方法示例:

@implementation LoadPageManager
RCT_EXPORT_MODULE()

// RN跳转Native //
// showCSTNativePage和前端交互的具体方法宏定义  //
// 在方法宏定义里面约定和前端交互的参数传递格式 //
RCT_EXPORT_METHOD(showCSTNativePage:(NSDictionary*)commonParames
                  disappearCallBack:(RCTResponseSenderBlock) disappearCallBack)
{
    NSLog(@"start load");
    //接收解析前端传参
    NSString* pagePath = [commonParames objectForKey:@"pagePath"];
    NSDictionary* aParams = [commonParames objectForKey:@"params"];
    NSString* aPostMessageEvent = [commonParames objectForKey:@"postMessageEvent"];
    NSInteger isCloseParent = [(NSNumber*)[commonParames objectForKey:@"isDestoryBeforePage"] intValue];
    NSString* aAppearEvent = [commonParames objectForKey:@"appearEvent"];
    
    NSMutableDictionary* aTmpDic = [NSMutableDictionary dictionaryWithDictionary:commonParames];
    // 以下代码不要删除 //
    NSMutableDictionary* aDic;
    if (aParams){
        aDic = [NSMutableDictionary dictionaryWithDictionary:aParams];
    }

    if (aPostMessageEvent){
        [aDic setValue:aPostMessageEvent forKey:PostEvent];
    }
   
    if (aAppearEvent){
        [aDic setValue:aAppearEvent forKey:AppearEvent];
    }
    // 以下代码不要删除 //
    //回调函数,页面返回时执行
    if (disappearCallBack){
        [aTmpDic setValue:disappearCallBack forKey:DisappearCallBack];
    }
    
    
    if (isCloseParent){
        [self RNBackAction];
    }
    // 转一下子为了和之前Hybrid 一致 //
    pagePath = [pagePath stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
    aDic = [self adpatToHybridParmers:[NSMutableDictionary dictionaryWithDictionary:aTmpDic]]; // 后期重做考虑去掉 //
    // 转一下子为了和之前Hybrid 一致 //
    
    dispatch_async(dispatch_get_main_queue(), ^{
       [[CHRDispatcher sharedDispatcher] invokeForPath:pagePath andParams: aDic];
    });
}


RCTBatchedBridge

为了桥接js跟native,native层引入了RCTBridge这个类负责双方的通信,不过真正起作用的是RCTBatchedBridge这个类,这个类应该算是比较重要的一个类了

通信在前端的实现:

RN层Libraries/BatchedBridge包下面有3个JS文件:BatchedBridge.js、MessageQueue.js、NativeModules.js,它们封装了通信桥在RN层的实现。

BatchedBridge.js

'use strict';

const MessageQueue = require('MessageQueue');

// MessageQueue can install a global handler to catch all exceptions where JS users can register their own behavior
// This handler makes all exceptions to be handled inside MessageQueue rather than by the VM at its origin
// This makes stacktraces to be placed at MessageQueue rather than at where they were launched
// The parameter __fbUninstallRNGlobalErrorHandler is passed to MessageQueue to prevent the handler from being installed
//
// __fbUninstallRNGlobalErrorHandler is conditionally set by the Inspector while the VM is paused for initialization
// If the Inspector isn't present it defaults to undefined and the global error handler is installed
// The Inspector can still call MessageQueue#uninstallGlobalErrorHandler to uninstalled on attach

const BatchedBridge = new MessageQueue(
  // $FlowFixMe
  typeof __fbUninstallRNGlobalErrorHandler !== 'undefined' &&
    __fbUninstallRNGlobalErrorHandler === true, // eslint-disable-line no-undef
);

// Wire up the batched bridge on the global object so that we can call into it.
// Ideally, this would be the inverse relationship. I.e. the native environment
// provides this global directly with its script embedded. Then this module
// would export it. A possible fix would be to trim the dependencies in
// MessageQueue to its minimal features and embed that in the native runtime.

Object.defineProperty(global, '__fbBatchedBridge', {
  configurable: true,
  value: BatchedBridge,
});

module.exports = BatchedBridge;


可以看到在BatchedBridge中创建了MessageQueue对象

MessageQueue.js

MessageQueue的构造方法

class MessageQueue {
  //Js模块注册添加到_lazyCallableModules
  _lazyCallableModules: {[key: string]: (void) => Object};
  //队列,分别存放要调用的模块数组、方法数组、参数数组与数组大小
  _queue: [number[], number[], any[], number];
  //回调函数数组,与_quueue一一对应,每个_queue中调用的方法,如果有回调方法,那么就在这个数组对应的下标上。
  _successCallbacks: (?Function)[];
  _failureCallbacks: (?Function)[];
  //调用函数ID,自动增加。
  _callID: number;
  _inCall: number;
  _lastFlush: number;
  _eventLoopStartTime: number;

  _debugInfo: {[number]: [number, number
  //Module Table,用于Native Module
  _remoteModuleTable: {[number]: string};
  //Method Table,用于Native Module
  _remoteMethodTable: {[number]: string[]};

  __spy: ?(data: SpyData) => void;

  __guard: (() => void) => void;

  constructor(shouldUninstallGlobalErrorHandler: boolean = false) {
    this._lazyCallableModules = {};
    this._queue = [[], [], [], 0];
    this._successCallbacks = [];
    this._failureCallbacks = [];
    this._callID = 0;
    this._lastFlush = 0;
    this._eventLoopStartTime = new Date().getTime();
    if (shouldUninstallGlobalErrorHandler) {
      this.uninstallGlobalErrorHandler();
    } else {
      this.installGlobalErrorHandler();
    }

    if (__DEV__) {
      this._debugInfo = {};
      this._remoteModuleTable = {};
      this._remoteMethodTable = {};
    }
    
    //绑定函数,也就是创建一个函数,使这个函数不论怎么调用都有同样的this值
    (this: any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind(
      this,
    );
    (this: any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind(
      this,
    );
    (this: any).flushedQueue = this.flushedQueue.bind(this);
    (this: any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind(
      this,
    );
  }
}


NativeModules.js

NativeModules描述了Module的结构,用于解析并生成Module。

Module的结构定义如下所示

type ModuleConfig = [
  string, /* name */
  ?Object, /* constants */
  Array, /* functions */
  Array, /* promise method IDs */
  Array, /* sync method IDs */
];


RN通过NativeModules来实现传输和接受消息
首先前端页面要引入NativeModules模块
import { NativeModules, Platform } from "react-native";


然后在我们需要使用的时候获取导出的模块,我们再用模块调用iOS的导出的函数名就可以了

let pageManager = NativeModules.LoadPageManager;
function nativeBridge(type, params, disappearCallBack) {
    pageManager[type](params, disappearCallBack);
}
//RN跳转native页面
function jumpNativePage(
    { pagePath, isDestoryBeforePage = 0, jumpParameter, appearEvent, postMessageEvent, ...other },
    disappearCallBack = (error, nativeData) => {} //回调函数
) {
    const error = checker(
        arguments,
        [
            {
                pagePath: "s|r",
                isDestoryBeforePage: "b",
                jumpParameter: "o",
                appearEvent: "s",
                postMessageEvent: "s",
                ...other
            },
            "f"
        ],
        "jumpNativePage"
    );
    // 线上环境报错不调起 Native 的 API
    if (error === "error") {
        return error;
    }
    let param = {
        pagePath,
        jumpParameter,
        appearEvent,
        postMessageEvent, //事件名称
        isDestoryBeforePage, //0:不关闭,1关闭
        ...other
    };
    nativeBridge("showCSTNativePage", param, disappearCallBack);
}
//showCSTNativePage约定此方法接收两个参数,一个是页面跳转需要的所有必填参数,是一个json对象,一个是页面返回时的回调函数,是Function类型


整个调用过程


3、native给RN主动发送事件

官网解释:
最好的方法是继承RCTEventEmitter,实现suppportEvents方法并调用self sendEventWithName:。

JavaScript代码可以创建一个包含你的模块的NativeEventEmitter实例来订阅这些事件

iOS端导出方法

@implementation NativeNotification
RCT_EXPORT_MODULE();

- (NSArray*) supportedEvents
{
    return @[AppearEvent];
}


RN端调用

import { NativeEventEmitter, NativeModules } from "react-native";
//NativeNotification是native监听事件的方法定义宏
//addListener是native监听事件的模块定义宏
const { NativeNotification } = NativeModules;
const eventManagerEmitter = new NativeEventEmitter(NativeNotification);

//在组件中使用
  componentWillMount() {
    this.listener = eventManagerEmitter.addListener('viewAppear', (nativeData)=>{
    //nativeData native端返回数据
    console.log(JSON.stringify(nativeData))
    });  //对应了原生端的名字
  }
  //记得remove
  componentWillUnmount() {
    this.listener && this.listener.remove();  
    


总结:
其实就是iOS端设置了桥接内容,将收到的通知内容传递给RN,然后RN监听有没有对应Name的事件名,有的话就处理

你可能感兴趣的:(ReactNative通信机制和渲染流程)