React Native 实现原理、渲染、通信机制总结

背景:

因为公司的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的源码:
    RCT_EXTERN void RCTRegisterModule(Class); 
      +(NSString *)moduleName                   
      {                                         
        return @ #js_name;                      
      }                                         
      +(void)load                               
      {                                         
        RCTRegisterModule(self);                
      }
    复制代码
  • 通过上图流程,native module注册最终定位到RCTCxxBridge._initModulesWithDispatchGroup
    - (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
    {
        NSMutableArray<Class> *moduleClassesByID = [NSMutableArray new];
        NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray new];
        NSMutableDictionary<NSString *, RCTModuleData *> *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),而是自己实现了一套跨平台通信机制。

猜你喜欢

转载自juejin.im/post/7018847318371729415