標籤:gif parent option 思路 array param ica 接下來 utf8
前言
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中全域變數中儲存起來。
React Native技術剖析(一)