软件开发者

React Native剖析

2017-06-02  本文已影响108人  Aaron丶丶

React Native 现在是异常的火爆,我司最近也完成了一个 React Native 编写的项目,现在已经提测审核。大家关心的苹果会不会拒绝RN ,会不会拒绝 CodePush ,我们会用实际行动告诉大家。

本文会介绍 React Native 的工作原理,让移动开发者从代码上了解框架。

React

React 是 facebook 出品一个前端框架,是目前最火的框架。以组件的形式组织项目结构,代替市面上的 MVC 框架。React 的出现解决了前端许多的痛点:

React 更像的是 MVC 中的 View 层,Model 和 Controller 的角色则由 Redux 代替( React 跟MVC 并无关系,仅仅是为了方便理解),单向数据流使得业务逻辑清晰明了。所以现在的
React 项目的常用体系是 React + Redux + webpack + ES6 + react-router 。我们的 Reat Native 项目使用的是 React + Redux + ES6 。

React Native

简单交代了 React 的背景,下面到了咱们的主角 —— React Native 。它可以看作是 React 的亲儿子,把全身的本领都传授了下去,儿子也挺争气,在 iOS 端跟 Android 端也有跨越性的突破。于是乎它就有了“跨平台”、“Javascript编写Native项目”史诗级的技能,他的表叔
Microsoft 对它也是疼爱有加,怕他挨 Native 欺负,给它做了一个叫CodePush 的装备,随时升级加修复,“热更新”这个标签又贴到了它的身上。现在的它是集万千宠爱于一身,要风得风,要雨得雨。我们现在就来扒一扒,看它究竟是何方神圣。

原理概述

引用React Native 从入门到原理中的一段话。

首先要明白的一点是,即使使用了 React Native,我们依然需要 UIKit 等框架,调用的是 Objective-C 代码。总之,JavaScript 只是辅助,它只是提供了配置信息和逻辑的处理结果。React Native 与 Hybrid 完全没有关系,它只不过是以 JavaScript 的形式告诉 Objective-C 该执行什么代码。

工作流程.png

直白的说,Javascript 在上层完成逻辑处理,然后通过 Javascript 引擎,实现 Javascript 与 Objective-C 交互,调用 Objective-C 中的原生UI组件,来实现页面的渲染。

JavaScript 是一种单线程的语言,它不具备自运行的能力,因此总是被动调用。很多介绍 React Native 的文章都会提到 “JavaScript 线程” 的概念,实际上,它表示的是 Objective-C 创建了一个单独的线程,这个线程只用于执行 JavaScript 代码,而且 JavaScript 代码只会在这个线程中执行。

React Native初始化

AppDelegatedidFinishLaunchingWithOptions方法中,我们找到了RN的入口。

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

首先创建了一个根控制器的View,然后将RN创建的view添加到窗口上显示。这个方法分为两步。在initWithBundleURL:moduleProvider:launchOptions这个方法内,React Native创建了一个实现 Objective-C 与 Javascript 交互的全局bridge,后续的所有交互全部都是通过这个桥接实现的。
第二步initWithBridge:moduleName:initialProperties方法中返回了刚才的RCTRootView

第一步初始化的核心方法是setUp。这个方法主要是创建了加载
main.jsbundle 的地址和创建了BatchedBridge。这个BatchedBridge才是真正的主角,它的主要作用就是读取 Javascript 对 Objective-C 的方法调用,而且它的内部持有一个
JavascriptExecutor 对象,用来执行 Javascript 代码。
- (void)setUp
{
...
[self createBatchedBridge];
[self.batchedBridge start];
...
}

RCTBatchedBridge

RCTBatchedBridge中最重要的就是Start方法。该方法主要包含以下几步:

  1. 读取Javascript代码
  2. 初始化需要暴露给js调用的Native模块
  3. 异步初始化 JS executor
  4. 异步初始化模块配置列表
  5. 将配置表传入JS端
  6. 调用JS代码

下面我们详细的解析下每个步骤

RCTRegisterModule

每个 module 都使用了RCTRegisterModule这个宏,在这个类的 load 方法的时候注册了自己的 moduleName 到 RCTModuleClasses这个数组中。这样,RN 遍历这个数组就能找到所有注册的 module 了。
RCTBridgeModule.h这个文件中我们可以看到实现。
#define RCT_EXPORT_MODULE(js_name)
RCT_EXTERN void RCTRegisterModule(Class);
+ (NSString *)moduleName { return @#js_name; }
+ (void)load { RCTRegisterModule(self); }

      void RCTRegisterModule(Class moduleClass)
      {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
          RCTModuleClasses = [NSMutableArray new];
        });

        RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
        @"%@ does not conform to the RCTBridgeModule protocol",
        moduleClass);

        // Register module
        [RCTModuleClasses addObject:moduleClass];
      }
`moduleName`方法,返回 `@#js_name`。`@# `是把宏参数 

js_name 转为字符串,若字符串为空则返回的就是空。在 RCTBridgeModuleNameForClass() 获取模块名的方法里,如果 moduleName 长度为0,那么就会调用 NSStringFromClass() 方法获取类名。

Tip:@#是将传入单字符参数名转换成字符。传送门--[define宏定义的#,##,@#及符号](http://blog.csdn.net/xdsoft365/article/details/5911596)。

executeSourceCode这个方法里,运行指定 url 的 js 代码。dev 模式下运行的是你的 bundleURL 下的代码,release 环境下运行的是你已经打包好的 jsbundle ,如果你使用了 codePush 对应的运行的是你 codePush 下发的 jsbundle。executeSourceCode开启了一个 NSRunLoop 不断的打印引入 native 模块中的 log 。

小结

本文一步步剖析了 ReactNative 这个伟大的框架,当然我这只是蜻蜓点水, ReactNative 还有许多值得我们学习的东西没有指出来,希望大家都去看看源码提高自己。后续,我会继续写文讲解下ReactNative如何将一个View页面展示出来和其他的一些小细节的东西。

PS

今天是2017-06-02,我们的APP已经过审,大家放心可用~

上一篇 下一篇

猜你喜欢

热点阅读