RN通訊機制和渲染流程
前言
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<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中不同狀態的描述。
元件的生命週期分為三個階段:
- 掛載(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互動
- 首先寫好前端jsx程式碼
- 把jsx程式碼解析成JavaScript程式碼
- OC讀取JS檔案
- 把JavaScript讀取出來,利用JavaScriptCore執行
- 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);
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渲染
七、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層的實現。
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型別
整個呼叫過程
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的事件名,有的話就處理