React Native技术剖析
Posted 伪装狙击手
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
returnname: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中全局变量中保存起来。
以上是关于React Native技术剖析的主要内容,如果未能解决你的问题,请参考以下文章