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

时间: 2024-08-08 17:48:59

React Native技术剖析(一)的相关文章

React Native 技术 开发跨平台 Native App 初探

转载请注明出处:http://blog.csdn.net/smartbetter/article/details/64190798 我们已经了解像 Titanium 和 PhoneGap 等框架,它们能让开发者用 Web 技术构建移动应用.这是一个优势,支持开发者使用原先网络和移动开发的相关技术.仅如此,相同的代码库经过小幅度的修改便能适用多个平台--这就是著名的一次编写,到处运行.然而,当涉及到构建应用的性能时,这些框架的缺点显露无遗,尽管它们有一些吸引力,但却一直更适用于构建原生应用.Rea

双平台真实开发GitHub App React Native技术全面掌握

第1章 项目需求分析.效果演示.功能分解,技术分解.开发计划制定,首页导航架构设计本章首先会对项目需求进行分析,然后对项目效果进行演示,让大家知道我们将要开发一款怎样的APP,接着,会制定一个项目开发计划,让大家明白我们将以怎样的方式开发这款APP,然后,会对这款APP做一个技术上的分解,让大家知道开发这款APP需要用到哪些技术:然后,会教你如何自定义组件,如何进行页面之间的导航数据传递等:... 第2章 项目基础功能开发(ListView.下拉刷新.上拉加载更多.APP启动引导流程)本章首先会

掌握React Native技术转型随意切换

第1章 课程预热React 与 React Native的区别,为什么学习 React Native. 第2章 初识 React Native快速搭建一个 React Native,了解 RN 的项目代码结构. 第3章 RN 入门知识学习学习React 的组件概念,ES5/ES6 两种组件形态,父子组件通信以及组件生命周期. 第4章 升级 React Native 重要补录这一章节是讲师针对课程上线以来,学生的常见问答,讲师做了一些总结补充视频. 第5章 项目初始准备作为实战编程的上手篇,需要又

React Native控件之PullToRefreshViewAndroid下拉刷新组件讲解

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50664323 本文出自:[江清清的博客] (一)前言 今天我们一起来看一下PullToRefreshViewAndroid下拉刷新组件讲解以及使用实例 刚创建的React Native技术交流群(282693535),欢迎各位大牛,React Native技术爱好者加入交流!同时博客左侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送! 该PullToRefr

【React Native开发】React Native进行签名打包成Apk

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50525976 本文出自:[江清清的博客] (一)前言 [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 前几节课程我们对于React Native的一些基础配置,开发工具以及调试,Android项目移植做了相关讲解,今天一起来学习一下另外一个比较重要的知识点,就是React Nativ

React Native专题-江清清

本React Native讲解专题:主要讲解了React Native开发,由基础环境搭建配置入门,基础,进阶相关讲解. 刚创建的React Native交流8群:533435865  欢迎各位大牛,React Native技术爱好者加入交流!同时博客右侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送! 关于React Native各种疑难杂症,问题深坑总结方案请点击查看: Mac和Windows安装搭建React Native环境教程如下: Mac OS X版本:Mac OS X安装R

【React Native开发】React Native控件之Touchable*系列组件详解(18)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50630984 本文出自:[江清清的博客] (一)前言 [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 今天我们一起来看一下Touchable*系列组件的使用详解,该系列组件包括四种分别为:TouchableHighlight,TouchableNativeFeedback,Touch

【React Native开发】React Native控件之ProgressBarAndroid进度条讲解(12)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50596367 本文出自:[江清清的博客] (一)前言 [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 今天我们一起来看一下进度加载条ProgressBarAndroid控件的讲解与基本使用. 刚创建的React Native技术交流群(282693535),欢迎各位大牛,React

【React Native开发】React Native移植原生Android项目

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50519677 本文出自:[江清清的博客] (一)前言 [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 前三节课程我们已经对于React Native For Android的环境搭建,IDE安装配置以及应用运行,调试相关的知识点做了讲解.今天我们来讲一个非常有用的知识点.移植我们已有