React Native 實現原理、渲染、通訊機制總結

語言: CN / TW / HK

背景:

因為公司的RN庫版本是0.4.x,調研後決定升級到0.6.x。之前沒有接觸過RN,在上手之前,現在網上大概進行一下基礎知識的學習,並將總結性的結論統一放在這裡,方便以後查閱。

參考:

【React Native】從原始碼一步一步解析它的實現原理

ReactNative原始碼解析——通訊機制詳解(1/2)

ReactNative原始碼解析——通訊機制詳解(2/2)

ReactNative原始碼解析——渲染機制詳解

整體架構、流程

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

  • 如果你瞭解這些東西的本質其實就很清楚了。動態或者指令碼語言要跟本地語言互通要具備如下幾點:
    1. 本地語言有一個runtime機制來對物件的方法呼叫進行動態解析。
    2. 本地語言一定有一個指令碼的解析引擎
    3. 建立一個指令碼語言到本地語言的對映表,KEY是指令碼語言認識的符號,VALUE是本地語言認識的符號。通過這個對映表來構建從指令碼到本地的呼叫。
  • 通過上述3個原則,無論是RN, JSPATCH, WEEKS, WEX都是使用同樣的機制。。沒有什麼神祕可言,也沒有什麼複雜度可言了。。。

啟動流程

React Native啟動流程(iOS)

React Native載入JS原始碼流程(iOS)

渲染原理

現場模型

通訊機制

ReactNative 的初始化

native module 註冊

  • 要將 Native module(類、介面)曝露給 JS,module需要實現RCTBridgeModule協議,並且在實現中要插入RCT_EXPORT_MODULE巨集。具體曝露的方法也需要通過RCT_EXPORT_METHOD巨集定義。
  • RCT_EXPORT_MODULE的原始碼: swift RCT_EXTERN void RCTRegisterModule(Class); +(NSString *)moduleName                   {                                         return @ #js_name;                      }                                         +(void)load                               {                                         RCTRegisterModule(self);                }
  • 通過上圖流程,native module註冊最終定位到RCTCxxBridge._initModulesWithDispatchGroup
    ```swift
    • (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup { NSMutableArray moduleClassesByID = [NSMutableArray new]; NSMutableArray moduleDataByID = [NSMutableArray new]; NSMutableDictionary *moduleDataByName = [NSMutableDictionary new];

      // Set up moduleData for automatically-exported modules for (Class moduleClass in RCTGetModuleClasses()) { NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass); moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];

      moduleDataByName[moduleName] = moduleData;
      [moduleClassesByID addObject:moduleClass];
      [moduleDataByID addObject:moduleData];
      

      }

      // Store modules _moduleDataByID = [moduleDataByID copy]; _moduleDataByName = [moduleDataByName copy]; _moduleClassesByID = [moduleClassesByID copy]; } `` * 上述程式碼第8RCTGetModuleClasses()即是獲取通過RCTRegisterModule註冊的 module 類(即所有曝露給 JS 的類) * 至此,所有需要曝露給 JS 的 module 都已註冊完成,並以RCTModuleData格式儲存在RCTCxxBridge中。 * 大部分 module 都是懶載入`,只有那些需要在主執行緒完成初始化以及有常量需要匯出的 module才會在註冊時例項化。

      JS 獲取 native module 資訊

      • 收集了所有曝露給 JS 的 module(也可稱之為生成了一份 native module 登錄檔);
      • 在 JS Context 中設定了nativeModuleProxy以及nativeFlushQueueImmediate
      • 初始化了相關的類,如:NativeToJsBridgeJsToNativeBridge以及JSCExecutor

      JS 呼叫 native module

      • NativeModules.moduleName — 該過程主要是獲取 native module 的資訊(moduleID、methodID),最終封裝為 JS object ({methodName: fn});
      • NativeModules.moduleName.methodName(params) — 執行呼叫。

總結

  • RN 專案中涉及多種語言,但 Native 與 JS 的通訊發生在C++JavaScript間;
  • 雙方具體負責通訊的分別是:Native 的JSCExecutor與 JS 的MessageQueue
  • 在 Native 側維護了一份曝露給 JS 的 module 登錄檔,在 JS 側維護了一份曝露給 Native 的 module 登錄檔;
  • RN 中 Native to JS 的通訊沒有使用JavaScriptCore提供的機制(blockJSExport),而是自己實現了一套跨平臺通訊機制。