首页 > 代码库 > React Native技术剖析(一)

React Native技术剖析(一)

前言

React Native(简称RN)的由来、优势、安装使用等等不在这里啰嗦,可以自行Google/百度。笔者整理了一下之前学习RN过程中记录的笔记,结合RN源代码来分析RN框架里面的一些技术思路,这对于理解和更好地使用RN都是很有益处的。由于水平有限,肯定有一些理解不对的地方欢迎指正。
今天主要讲一下,RN初始化过程是什么步骤,都做了哪些事情。

RN初始化过程

以iOS为例,RN的渲染主要在RCTRootView中,初始化代码非常简单,就是创建RootVIew对象. (由于函数调用层次比较深,一层层讲解很容易不清楚当前在那个函数,请读者谅解)

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation                                                 moduleName:@"LarkRN"                                            initialProperties:nil                                                 launchOptions:launchOptions];

我们看一下RCTRootView的initWithBundleURL函数做了什么:
函数中创建一个Bridge对象,并调用initWithBridge函数。

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions
{
  RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
                                            moduleProvider:nil
                                             launchOptions:launchOptions];
      return [self initWithBridge:bridge moduleName:moduleNameinitialProperties:initialProperties];
}

Bridge对象创建过程中又调用了[self setUp],setUp函数调用createBatchedBridge函数创建了一个BatchedBridge对象

- (void)createBatchedBridge
{
  self.batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
}

BatchedBridge对象初始化中又调用了[self start],Start函数调用moduleConfig函数

    config = [weakSelf moduleConfig];

BatchedBridge对象的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);

每个原生模块的配置信息由RCTModuleData 对象config属性管理,config是个数组,其中的元素依次为模块名,导出常量数组,导出函数数组,导出异步函数(类型为RCTFunctionTypePromise的函数)索引数组

NSDictionary<NSString *, id> *constants;
NSMutableArray<NSString *> *methods;
NSMutableArray<NSNumber *> *asyncMethods; 

 NSMutableArray *config = [NSMutableArray new];
  [config addObject:self.name];
  if (constants.count) {
    [config addObject:constants];
  }
  if (methods) {
    [config addObject:methods];
    if (asyncMethods) {
      [config addObject:asyncMethods];
    }
  }

BatchedBridge对象Start函数调用injectJSONConfiguration函数

  [weakSelf injectJSONConfiguration:config onComplete:nil

BatchedBridge对象injectJSONConfiguration函数就是向JSC中添加一个全局变量__fbBatchedBridgeConfig

 [_javaScriptExecutor injectJSONText:configJSON
                  asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
                             callback:onComplete];

接下来执行BatchedBridge对象的executeSourceCode函数,执行RN Bundle文件(JS代码)

 [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError)
  {…此处省略}

BatchedBridge对象的executeSourceCode函数调用JSC的executeApplicationScript函数来运行JS代码

 [_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {…}

JSC对象的executeApplicationScript函数将下载的RN Bundle文件放入JSC中执行

    RCTJSCExecutor *strongSelf = weakSelf;
    JSValueRef jsError = NULL;
    JSStringRef execJSString = JSStringCreateWithUTF8CString((const char *)script.bytes);
    JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, _bundleURL, 0, &jsError);
    JSStringRelease(execJSString);

JS的BatchedBridge对象实际上是MessageQueue对象,使用前面的__fbBatchedBridgeConfig(原生模块配置列表)变量进行初始化

    constBatchedBridge=new MessageQueue(
        ()=>global.__fbBatchedBridgeConfig
    );

MessageQueue对象的初始化过程中创建一个RemoteModules属性

    lazyProperty(this,‘RemoteModules‘,()=>{
        Let {remoteModuleConfig}=configProvider();
        Let modulesConfig=this._genModulesConfig(remoteModuleConfig);
        Let modules=this._genModules(modulesConfig);
        this._genLookupTables(
            modulesConfig,this._remoteModuleTable,this._remoteMethodTable
        );
        returnmodules;
    });
    _genModules(remoteModules){
        Let modules={};
        remoteModules.forEach((config,moduleID)=>{
        Let info=this._genModule(config,moduleID);
        if(info){
            modules[info.name]=info.module;
        }
        });
        Return modules;
    }

MessageQueue对象的_genModules函数生成模型信息对象,key值为模型名,如果模块没有导出任何属性(常量或函数),则仅记录模块ID;处理模型中导出的函数,对函数进行包装。

    _genModule(config,moduleID):?Object{
        Let moduleName,constants,methods,asyncMethods,syncHooks;
        if(moduleHasConstants(config)){
            [moduleName,constants,methods,asyncMethods,syncHooks]=config;
        }else{
            [moduleName,methods,asyncMethods,syncHooks]=config;
        }

        Let module={};
        Methods && methods.forEach((methodName,methodID)=>{
        Const isAsync = asyncMethods && arrayContains(asyncMethods,methodID);
        Const isSyncHook = syncHooks && arrayContains(syncHooks,methodID);
        Const methodType=isAsync? MethodTypes.remoteAsync:
        isSyncHook ? MethodTypes.syncHook:
        MethodTypes.remote;

        module[methodName]=this._genMethod(moduleID,methodID,methodType);
        });
        Object.assign(module,constants);
        if(!constants&&!methods&&!asyncMethods){
            module.moduleID=moduleID;
        }
        return{name:moduleName,module};
    }

MessageQueue对象的_genLookupTables函数生成模块名检索表和函数检索表

    _genLookup(config,moduleID,moduleTable,methodTable){
        Let moduleName,methods;
        if(moduleHasConstants(config)){
            [moduleName,,methods]=config;
        }else{
            [moduleName,methods]=config;
        }
        moduleTable[moduleID]=moduleName;
        methodTable[moduleID]=Object.assign({},methods);
    } 

MessageQueue对象registerCallableModule注册JS端可调用模块,记录每个模块名和可用的函数列表,供原生端调用

    BatchedBridge.registerCallableModule(‘Systrace‘,Systrace);
    registerCallableModule(name,methods){
        this._callableModules[name]=methods;
    }

MessageQueue对象初始化完成后,会放入到一个全局变量”__fbBatchedBridge”中,供后续使用。

    Object.defineProperty(global,‘__fbBatchedBridge‘,{value:BatchedBridge});

到此,RN初始化过程全部结束。简单来说,就是收集原生和JS模块配置信息,然后生成每个模块及其可用函数的检索表, 并放入JSC中全局变量中保存起来。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    React Native技术剖析(一)