ReactNative 的實現(xiàn)原理是什么?
ReactNative 的本質是在 JS 端編寫 React 代碼圆凰,通過 JavaScriptCore 引擎杈帐,把 JS 端編寫的組件和事件轉換成 Native 原生組件進行渲染。所以理解 ReactNative 的實現(xiàn)原理专钉,主要是搞清楚 JS 端與 Native 端的通信原理挑童。
要保證 JavaScript 和 Native 具備向對方通信的能力,首先需要搞清楚 RN 在啟動的時候都做了些啥跃须,這篇文章就是基于 ReactNatve 源碼分析的基礎上站叼,對 ReactNative 的啟動流程進行深入淺出的分析。
概述
ReactNative 啟動是從創(chuàng)建一個 RCTRootView 作為入口視圖容器開始運作的菇民,創(chuàng)建 RCTRootView 時尽楔,會先創(chuàng)建一個 JSBridge 作為 Native 端與 JS 端交互的橋梁。整個 RN 的啟動工作基本都是在創(chuàng)建 JSBridge 時做的玉雾。
JSBridge 的核心是 RCTBatchedBridge 的,JSBridge 的工作主要在 RCTBatchedBridge 初始化中處理轻要。啟動流程采用 GCD 來進行多線程操作复旬,其中大部分耗時操作是在并發(fā)隊列
com.facebook.react.RCTBridgeQueue
中進行的。RCTBatchedBridge 啟動主要進行六個準備工作:
1.加載 JSBundle 代碼(并行隊列異步執(zhí)行)
2.初始化 Native Modules(同步執(zhí)行)
3.初始化 JSCExecutor(與第4步在同一并行隊列同時執(zhí)行)
4.創(chuàng)建 Module 配置表(與第3步在同一并行隊列同時執(zhí)行)
5.注入 Module 配置信息到 JSCExecutor(第3冲泥、4步執(zhí)行完再執(zhí)行)
6.執(zhí)行 JSBundle 代碼(前5步都執(zhí)行完再執(zhí)行)
1.加載 JSBundle 代碼
JSBundle: React代碼經(jīng)過打包后生成的JavaScript 源碼和圖片等資源文件
熱更新實質:將JSBundle放在服務器驹碍,在合適時機(啟動/切換前后臺/打開某個頁面按需加載等)壁涎,將JSBundle下載到本地來實現(xiàn)熱更新
將 JavaScript 源碼加載到內存中,方便之后注入和執(zhí)行志秃,這一步中怔球,React 中的 JSX 語法已經(jīng)轉換成 JavaScript。執(zhí)行代碼如下:
[RCTBatchedBridge loadSource: onProgress:]
2.初始化 Native Modules
同步初始化所有不能被懶惰加載的供 JS 端調用的 Native 模塊浮还。
Native Modules: 所有Native需要暴露給 JS 的類(即標有宏:RCT_EXPORT_MODULE()的類)
主要作用:找到所有 Native 需要暴露給 JavaScript 的類(即被標記有宏:RCT_EXPORT_MODULE()
的類)竟坛,方便后面把這些模塊信息注入 JS 端。執(zhí)行代碼如下:
[RCTBatchedBridge initModulesWithDispatchGroup:]
3.初始化 JSCExecutor
JSCExecutor:JavaScriptCore引擎钧舌,負責JS端和Native端的通信
執(zhí)行代碼如下:
[RCTBatchedBridge setUpExecutor]
1.初始化時担汤,創(chuàng)建一個優(yōu)先級跟主線程優(yōu)先級同級的單獨的 JS 線程,同時創(chuàng)建一個 Runloop洼冻,讓 JS 線程能循環(huán)執(zhí)行不會退出崭歧。
2.初始化時,通過 JavaScriptCore 作為引擎撞牢,創(chuàng)建 JS 執(zhí)行的上下文環(huán)境率碾,并向 JS 上下文中注入 JS 與 Native 通信的方法。其中屋彪,在 Native 端實現(xiàn)的供 JS 端調用的幾個重要方法:
nativeRequireModuleConfig
:js 獲取 native module 配置表所宰。RN 并沒有保存整個的 Native 方法配置表,而僅僅保存了模塊的名字撼班。這個回調就是 JS 每次根據(jù) module 名可以查到這個 module 的模塊配置信息nativeFlushQueueImmediate
: js 觸發(fā) native 進行隊列消息處理歧匈。一般來說 JS 并不會主動調用 Native 的方法,而是等著 Native 定時器每隔一段時間到 JS 的 eventQueue 中去取砰嘁,取出來以后批量執(zhí)行件炉。而 nativeFlushQueueImmediate 就是讓 JS 直接調用 Native 的方法而不用等待。nativeCallSyncHook
:同步調用
4.創(chuàng)建 Module 配置表
Module 配置表:
1.把所有模塊信息集中收集起來矮湘,保存到一個數(shù)組中斟冕,經(jīng)過序列化后,注入到JS中缅阳。
2.JS 端通過 Native 端注入的 nativeRequireModuleConfig 方法磕蛇,根據(jù) module 名可以查詢該模塊配置信息。
創(chuàng)建 Module 配置表十办,與初始化 JSCExecutor 的操作一起被加入并發(fā)隊列com.facebook.react.RCTBridgeQueue
異步進行秀撇。執(zhí)行代碼如下:
[RCTBatchedBridge moduleConfig]
- (NSString *)moduleConfig
{
NSMutableArray<NSArray *> *config = [NSMutableArray new];
for (RCTModuleData *moduleData in _moduleDataByID) {
if (self.executorClass == [RCTJSCExecutor class]) {
[config addObject:@[moduleData.name]];
} else {
[config addObject:RCTNullIfNil(moduleData.config)];
}
}
return RCTJSONStringify(@{
@"remoteModuleConfig": config,
}, NULL);
}
獲取 moduleConfig 時,會根據(jù)設置不同的 executorClass向族,收集的模塊信息會有區(qū)別呵燕,如果是RCTJSCExecutor
類型,Native 端只保存模塊的名字件相,JS 端通過 Native 端注入的nativeRequireModuleConfig
方法再扭,根據(jù) module 名可以查到這個 module 的模塊配置信息氧苍。如果不是RCTJSCExecutor
類型,通過moduleData.config
鏈接到[RCTModuleData config]
泛范,會把 module 名让虐、常量、函數(shù)等 native module 的配置信息都保存起來罢荡。
5.注入 Module 配置信息到 JS 端
執(zhí)行 [RCTBatchedBridge injectJSONConfiguration:onComplete:]
當 初始化 JSCExecutor 和 創(chuàng)建 Module 配置表 都準備好后赡突,會將 module 的模塊配置信息注入 JS 端。
6.執(zhí)行 JSBundle 代碼
執(zhí)行 [RCTBatchedBridge executeSourceCode:]
以上五步操作都執(zhí)行完成后柠傍,執(zhí)行 JSBundle 中的 JavaScript 源碼麸俘。至此,JavaScript 和 Objective-C 都具備了向對方交互的能力惧笛,啟動流程的準備工作算是全部完成了从媚。