React Native技术剖析(一)

来源:互联网 发布:淘宝窗帘拍摄地点 编辑:程序博客网 时间:2024/05/22 12:53

前言

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中全局变量中保存起来。

1 0
原创粉丝点击