1. 程式人生 > >RN通訊機制和渲染流程

RN通訊機制和渲染流程

前言

React Native與傳統的HybirdApp最大區別就是拋開WebView,使用JSC+原生元件的方式進行渲染,那麼整個App啟動/渲染流程又是怎樣的呢?

一、整體框架

RN 這套框架讓 JS開發者可以大部分使用JS程式碼就可以構建一個跨平臺APP;
image

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<AppConfig>) 

//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(
  <Page />,
  document.getElementById('app')
);

4、RN元件的生命週期

像 Android和iOS 開發一樣,React Native(RN) 中的元件也有生命週期(Lifecycle);
當應用啟動,React Native在記憶體中維護著一個虛擬DOM,元件的生命週期就是指元件初始化並掛載到虛擬DOM為起始,到元件從虛擬DOM解除安裝為終結。生命週期的方法就是元件在虛擬DOM中不同狀態的描述。
image
元件的生命週期分為三個階段:
- 掛載(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框架,通過它就能實現JS和OC互動

  1. 首先寫好前端jsx程式碼
  2. 把jsx程式碼解析成JavaScript程式碼
  3. OC讀取JS檔案
  4. 把JavaScript讀取出來,利用JavaScriptCore執行
  5. javaScript程式碼返回一個數組,陣列中會描述OC物件,OC物件的屬性,OC物件所需要執行的方法,這樣就能讓這個物件設定屬性,並且呼叫方法

image

五、RN啟動流程(iOS)

流程圖:
image

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);

2、建立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渲染

image

七、RN通訊機制

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層的實現。

image

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<string>, /* functions */
  Array<number>, /* promise method IDs */
  Array<number>, /* 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型別

整個呼叫過程
image

3、native給RN主動傳送事件

官網解釋:
最好的方法是繼承RCTEventEmitter,實現suppportEvents方法並呼叫self sendEventWithName:。

JavaScript程式碼可以建立一個包含你的模組的NativeEventEmitter例項來訂閱這些事件

iOS端匯出方法

@implementation NativeNotification
RCT_EXPORT_MODULE();

- (NSArray<NSString*>*) 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的事件名,有的話就處理