React Native 从入门到原理

动态配置

因为 AppStore 审核周期的限制,如何动态的更改 app 成为了永恒的话题。不管采用何种方式,咱们的流程老是能够归结为如下三部曲:“从 Server 获取配置 --> 解析 --> 执行native代码”。javascript

不少时候,咱们自觉或者不自觉的利用 JSON 文件实现动态配置的效果,它的核心流程是:html

  1. 经过 HTTP 请求获取 JSON 格式的配置文件。
  2. 配置文件中标记了每个元素的属性,好比位置,颜色,图片 URL 等。
  3. 解析完 JSON 后,咱们调用 Objective-C 的代码,完成 UI 控件的渲染。

经过这种方法,咱们实现了在后台配置 app 的展现样式。从本质上来讲,移动端和服务端约定了一套协议,可是协议内容严重依赖于应用内要展现的内容,不利于拓展。也就是说,若是业务要求频繁的增长或修改页面,这套协议很难应付。前端

最重要的是,JSON 只是一种数据交换的格式,说白了,咱们就是在解析文本数据。这就意味着它只适合提供一些配置信息,而不方便提供逻辑信息。举个例子,咱们从后台能够配置颜色,位置等信息,但若是想要控制 app 内的业务逻辑,就很是复杂了。java

记住,咱们只是在解析字符串,它彻底不具有运行和调试的能力。react

React

不妨暂时抛弃移动端的烦恼,来看看前端的“新玩意”。android

背景

做为前端小白,我之前对前端的理解是这样的:git

  • 用 HTML 建立 DOM,构建整个网页的布局、结构
  • 用 CSS 控制 DOM 的样式,好比字体、字号、颜色、居中等
  • 用 JavaScript 接受用户事件,动态的操控 DOM

在这三者的配合下,几乎全部页面上的功能都能实现。但也有比较不爽地方,好比我想动态修改一个按钮的文字,我须要这样写:github

<button type="button" id="button" onclick="onClick()">old button</button>

而后在 JavaScript 中操做 DOM:objective-c

<script>
function onClick() {
  document.getElementById('button').innerHTML='new button';
}
</script>

能够看到,在 HTML 和 JavaScript 代码中,id 和 onclick 事件触发的函数必须彻底对应,不然就没法正确的响应事件。若是想知道一个 HTML 标签会如何被响应,咱们还得跑去 JavaScript 代码中查找,这种原始的配置方式让我以为很是不爽。算法

初识 React

随着 FaceBook 推出了 React 框架,这个问题获得了大幅度改善。咱们能够把一组相关的 HTML 标签,也就是 app 内的 UI 控件,封装进一个组件(Component)中,我从阮一峰的 React 教程中摘录了一段代码:

var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

若是你想问:“为何 JavaScript 代码里面出现了 HTML 的语法”,那么恭喜你已经初步体会到 React 的奥妙了。这种语法被称为 JSX,它是一种 JavaScript 语法拓展。JSX 容许咱们写 HTML 标签或 React 标签,它们终将被转换成原生的 JavaScript 并建立 DOM。

在 React 框架中,除了能够用 JavaScript 写 HTML 之外,咱们甚至能够写 CSS,这在后面的例子中能够看到。

理解 React

前端界老是喜欢创造新的概念,仿佛谁说的名词更晦涩,谁的水平就越高。若是你和当时的我同样,听到 React 这个概念一脸懵逼的话,只要记住如下定义便可:

React 是一套能够用简洁的语法高效绘制 DOM 的框架

上文已经解释过了何谓“简洁的语法”,由于咱们能够暂时放下 HTML 和 CSS,只关心如何用 JavaScript 构造页面。

所谓的“高效”,是由于 React 首创了 Virtual DOM 机制。Virtual DOM 是一个存在于内存中的 JavaScript 对象,它与 DOM 是一一对应的关系,也就是说只要有 Virtual DOM,咱们就能渲染出 DOM。

当界面发生变化时,得益于高效的 DOM Diff 算法,咱们可以知道 Virtual DOM 的变化,从而高效的改动 DOM,避免了从新绘制 DOM。

固然,React 并非前端开发的所有。从以前的描述也能看出,它专一于 UI 部分,对应到 MVC 结构中就是 View 层。要想实现完整的 MVC 架构,还须要 Model 和 Controller 的结构。在前端开发时,咱们能够采用 Flux 和 Redux 架构,它们并不是框架(Library),而是和 MVC 同样都是一种架构设计(Architecture)。

若是不从事前端开发,就不用深刻的掌握 Flux 和 Redux 架构,但理解这一套体系结构对于后面理解 React Native 很是重要

React Native

分别介绍完了移动端和前端的背景知识后,本文的主角——React Native 终于要登场了。

融合

前面咱们介绍了移动端经过 JSON 文件传递信息的不足之处:只能传递配置信息,没法表达逻辑。从本质上讲,这是由于 JSON 毕竟只是纯文本,它缺少像编程语言那样的运行能力。

而 React 在前端取得突破性成功之后,JavaScript 布道者们开始试图一统三端。他们利用了移动平台可以运行 JavaScript 代码的能力,而且发挥了 JavaScript 不只仅能够传递配置信息,还能够表达逻辑信息的优势。

当痛点赶上特色,二者一拍即合,因而乎:

一个基于 JavaScript,具有动态配置能力,面向前端开发者的移动端开发框架,React Native,诞生了!

看到了么,这是一个面向前端开发者的框架。它的宗旨是让前端开发者像用 React 写网页那样,用 React Native 写移动端应用。这就是为何 React Native 自称:

Learn once,Write anywhere!

而非不少跨平台语言,项目所说的:

Write once, Run anywhere!

React Native 但愿前端开发者学习完 React 后,可以用一样的语法、工具等,分别开发安卓和 iOS 平台的应用而且不用一行原生代码。

若是用一个词归纳 React Native,那就是:Native 版本的 React

原理概述

React Native 不是黑科技,咱们写的代码老是以一种很是合理,能够解释的方式的运行着,只是绝大多数人没有理解而已。接下来我以 iOS 平台为例,简单的解释一下 React Native 的原理。

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

其次,React Native 可以运行起来,全靠 Objective-C 和 JavaScript 的交互。对于没有接触过 JavaScript 的人来讲,很是有必要理解 JavaScript 代码如何被执行。

咱们知道 C 系列的语言,通过编译,连接等操做后,会获得一个二进制格式的可执行文,所谓的运行程序,实际上是运行这个二进制程序。

而 JavaScript 是一种脚本语言,它不会通过编译、连接等操做,而是在运行时才动态的进行词法、语法分析,生成抽象语法树(AST)和字节码,而后由解释器负责执行或者使用 JIT 将字节码转化为机器码再执行。整个流程由 JavaScript 引擎负责完成。

苹果提供了一个叫作 JavaScript Core 的框架,这是一个 JavaScript 引擎。经过下面这段代码能够简单的感觉一下 Objective-C 如何调用 JavaScript 代码:

JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];

这里的 JSContext 指的是 JavaScript 代码的运行环境,经过 evaluateScript 便可执行 JavaScript 代码并获取返回结果。

JavaScript 是一种单线程的语言,它不具有自运行的能力,所以老是被动调用。不少介绍 React Native 的文章都会提到 “JavaScript 线程” 的概念,实际上,它表示的是 Objective-C 建立了一个单独的线程,这个线程只用于执行 JavaScript 代码,并且 JavaScript 代码只会在这个线程中执行。

Objective-C 与 JavaScript 交互

提到 Objective-C 与 JavaScript 的交互,不得不推荐 bang神的这篇文章:React Native通讯机制详解。虽然其中很多细节都已通过时,可是总体的思路值得学习。

本节主要分析 Objective-C 与 JavaScript 交互时的整理逻辑与流程,下一节将经过源码来分析具体原理。

JavaScript 调用 Objective-C

因为 JavaScript Core 是一个面向 Objective-C 的框架,在 Objective-C 这一端,咱们对 JavaScript 上下文知根知底,能够很容易的获取到对象,方法等各类信息,固然也包括调用 JavaScript 函数。

真正复杂的问题在于,JavaScript 不知道 Objective-C 有哪些方法能够调用。

React Native 解决这个问题的方案是在 Objective-C 和 JavaScript 两端都保存了一份配置表,里面标记了全部 Objective-C 暴露给 JavaScript 的模块和方法。这样,不管是哪一方调用另外一方的方法,实际上传递的数据只有 ModuleIdMethodId 和 Arguments 这三个元素,它们分别表示类、方法和方法参数,当 Objective-C 接收到这三个值后,就能够经过 runtime 惟一肯定要调用的是哪一个函数,而后调用这个函数。

再次重申,上述解决方案只是一个抽象概念,可能与实际的解决方案有微小差别,好比实际上 Objective-C 这一端,并无直接保存这个模块配置表。具体实现将在下一节中随着源码一块儿分析。

闭包与回调

既然说到函数互调,那么就不得不提到回调了。对于 Objective-C 来讲,执行完 JavaScript 代码再执行 Objective-C 回调毫无难度,难点依然在于 JavaScript 代码调用 Objective-C 以后,如何在 Objective-C 的代码中,回调执行 JavaScript 代码。

目前 React Native 的作法是:在 JavaScript 调用 Objective-C 代码时,注册要回调的 Block,而且把 BlockId 做为参数发送给 Objective-C,Objective-C 收到参数时会建立 Block,调用完 Objective-C 函数后就会执行这个刚刚建立的 Block。

Objective-C 会向 Block 中传入参数和 BlockId,而后在 Block 内部调用 JavaScript 的方法,随后 JavaScript 查找到当时注册的 Block 并执行。

图解

好吧,若是你是新手,而且坚持读到了这里,估计已经懵逼了。不要担忧,与 JavaScript 的交互确实不是一会儿可以彻底理清楚的,你能够先参考这个示意图:

交互流程

注:

  1. 本图由 bang 的文章中的图片修改而来
  2. 本图只是一个简单的示意图,不建议当作时序图使用,请参考下一节源码分析。
  3. Objective-C 和 JavaScript 的交互老是由前者发起,本图为了简化,省略了这一步骤。

React Native 源码分析

要想深刻理解 React Native 的工做原理,有两个部分有必要阅读一下,分别是初始化阶段和方法调用阶段。

为了提炼出代码的核心含义,我会在不改变代码意图的基础上对它作一些删改,以便阅读。

写这篇文章是,React Native 还处于 0.27 版本,因为在 1.0 以前的变更幅度相对较大,所以下面的源码分析极可能随着 React Native 的演变而过期。但无论什么时候,把下面的源码读一遍都有助于你加深对 React Native 原理的理解。

初始化 React Native

每一个项目都有一个入口,而后进行初始化操做,React Native 也不例外。一个不含 Objective-C 代码的项目留给咱们的惟一线索就是位于 AppDelegate文件中的代码:

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

用户能看到的一切内容都来源于这个 RootView,全部的初始化工做也都在这个方法内完成。

在这个方法内部,在建立 RootView 以前,React Native 实际上先建立了一个 Bridge 对象。它是 Objective-C 与 JavaScript 交互的桥梁,后续的方法交互彻底依赖于它,而整个初始化过程的最终目的其实也就是建立这个桥梁对象。

初始化方法的核心是 setUp 方法,而 setUp 方法的主要任务则是建立 BatchedBridge

BatchedBridge 的做用是批量读取 JavaScript 对 Objective-C 的方法调用,同时它内部持有一个 JavaScriptExecutor,顾名思义,这个对象用来执行 JavaScript 代码。

建立 BatchedBridge 的关键是 start 方法,它能够分为五个步骤:

  1. 读取 JavaScript 源码
  2. 初始化模块信息
  3. 初始化 JavaScript 代码的执行器,即 RCTJSCExecutor 对象
  4. 生成模块列表并写入 JavaScript 端
  5. 执行 JavaScript 源码

咱们逐个分析每一步完成的操做:

读取 JavaScript 源码

这一部分的具体代码实现没有太大的讨论意义。咱们只要明白,JavaScript 的代码是在 Objective-C 提供的环境下运行的,因此第一步就是把 JavaScript 加载进内存中,对于一个空的项目来讲,全部的 JavaScript 代码大约占用 1.5 Mb 的内存空间。

须要说明的是,在这一步中,JSX 代码已经被转化成原生的 JavaScript 代码。

初始化模块信息

这一步在方法 initModulesWithDispatchGroup: 中实现,主要任务是找到全部须要暴露给 JavaScript 的类。每个须要暴露给 JavaScript 的类(也成为 Module,如下不做区分)都会标记一个宏:RCT_EXPORT_MODULE,这个宏的具体实现并不复杂:

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

这样,这个类在 load 方法中就会调用 RCTRegisterModule 方法注册本身:

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

  [RCTModuleClasses addObject:moduleClass];
}

所以,React Native 能够经过 RCTModuleClasses 拿到全部暴露给 JavaScript 的类。下一步操做是遍历这个数组,而后生成 RCTModuleData 对象:

for (Class moduleClass in RCTGetModuleClasses()) {
    RCTModuleData *moduleData = [[RCTModuleData alloc]initWithModuleClass:moduleClass                                                                      bridge:self];
    [moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
}

能够想见,RCTModuleData 对象是模块配置表的主要组成部分。若是把模块配置表想象成一个数组,那么每个元素就是一个 RCTModuleData 对象。

这个对象保存了 Module 的名字,常量等基本信息,最重要的属性是一个数组,保存了全部须要暴露给 JavaScript 的方法。

暴露给 JavaScript 的方法须要用 RCT_EXPORT_METHOD 这个宏来标记,它的实现原理比较复杂,有兴趣的读者能够自行阅读。简单来讲,它为函数名加上了 __rct_export__ 前缀,再经过 runtime 获取类的函数列表,找出其中带有指定前缀的方法并放入数组中:

- (NSArray<id<RCTBridgeMethod>> *)methods{
    unsigned int methodCount;
    Method *methods = class_copyMethodList(object_getClass(_moduleClass), &methodCount); // 获取方法列表
    for (unsigned int i = 0; i < methodCount; i++) {
        RCTModuleMethod *moduleMethod = /* 建立 method */
        [_methods addObject:moduleMethod];
      }
    }
    return _methods;
}

所以 Objective-C 管理模块配置表的逻辑是:Bridge 持有一个数组,数组中保存了全部的模块的 RCTModuleData 对象。只要给定 ModuleId 和 MethodId 就能够惟一肯定要调用的方法。

初始化 JavaScript 代码的执行器,即 RCTJSCExecutor 对象

经过查看源码能够看到,初始化 JavaScript 执行器的时候,addSynchronousHookWithName 这个方法被调用了屡次,它其实向 JavaScript 上下文中添加了一些 Block 做为全局变量:

- (void)addSynchronousHookWithName:(NSString *)name usingBlock:(id)block {
    self.context.context[name] = block;
}

有些同窗读源码时可能会走进一个误区,若是在 Block 中打一个断点就会发现,Block 实际上是被执行了,但却找不到任何可以执行 Block 的代码。

这实际上是由于这个 Block 并不是由 Objective-C 主动调用,而是在第五步执行 JavaScript 代码时,由 JavaScript 在上下文中获取到 Block 对象并调用,有兴趣的读者能够自行添加断点并验证。

这里咱们须要重点注意的是名为 nativeRequireModuleConfig 的 Block,它在 JavaScript 注册新的模块时调用:

get: () => {
    let module = RemoteModules[moduleName];
    const json = global.nativeRequireModuleConfig(moduleName); // 调用 OC 的 Block
    const config = JSON.parse(json); // 解析 json
    module = BatchedBridge.processModuleConfig(config, module.moduleID); // 注册 config
    return module;
},

这就是模块配置表可以加载到 JavaScript 中的原理。

另外一个值得关注的 Block 叫作 nativeFlushQueueImmediate。实际上,JavaScript 除了把调用信息放到 MessageQueue 中等待 Objective-C 来取之外,也能够主动调用 Objective-C 的方法:

if (global.nativeFlushQueueImmediate &&
    now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
    global.nativeFlushQueueImmediate(this._queue); // 调用 OC 的代码
}

目前,React Native 的逻辑是,若是消息队列中有等待 Objective-C 处理的逻辑,并且 Objective-C 超过 5ms 都没有来取走,那么 JavaScript 就会主动调用 Objective-C 的方法:

[self addSynchronousHookWithName:@"nativeFlushQueueImmediate" usingBlock:^(NSArray<NSArray *> *calls){
    [self->_bridge handleBuffer:calls batchEnded:NO];
}];

这个 handleBuffer 方法是 JavaScript 调用 Objective-C 方法的关键,在下一节——方法调用中,我会详细分析它的实现原理。

通常状况下,Objective-C 会定时、主动的调用 handleBuffer 方法,这有点相似于轮询机制:

// 每一个一段时间发生一次:
Objective-C:嘿,JavaScript,有没有要调用个人方法呀?
JavaScript:有的,你从 MessageQueue 里面取出来。

然而因为卡顿或某些特殊缘由,Objective-C 并不能老是保证可以准时的清空 MessageQueue,这就是为何 JavaScript 也会在必定时间后主动的调用 Objective-C 的方法。查看上面 JavaScript 的代码能够发现,这个等待时间是 5ms。

请紧紧记住这个 5ms,它告诉咱们 JavaScript 与 Objective-C 的交互是存在必定开销的,否则就不会等待而是每次都马上发起请求。其次,这个时间开销大约是毫秒级的,不会比 5ms 小太多,不然等待这么久就意义不大了。

生成模块配置表并写入 JavaScript 端

复习一下 nativeRequireModuleConfig 这个 Block,它能够接受 ModuleName 而且生成详细的模块信息,但在前文中咱们没有提到 JavaScript 是如何知道 Objective-C 要暴露哪些类的(目前只是 Objective-C 本身知道)。

这一步的操做就是为了让 JavaScript 获取全部模块的名字:

- (NSString *)moduleConfig{
    NSMutableArray<NSArray *> *config = [NSMutableArray new];
    for (RCTModuleData *moduleData in _moduleDataByID) {
      [config addObject:@[moduleData.name]];
    }
}

查看源码能够发现,Objective-C 把 config 字符串设置成 JavaScript 的一个全局变量,名字叫作:__fbBatchedBridgeConfig

执行 JavaScript 源码

这一步也没什么技术难度能够,代码已经加载进了内存,该作的配置也已经完成,只要把 JavaScript 代码运行一遍便可。

运行代码时,第三步中所说的那些 Block 就会被执行,从而向 JavaScript 端写入配置信息。

至此,JavaScript 和 Objective-C 都具有了向对方交互的能力,准备工做也就所有完成了。

画了一个简陋的时序图以供参考:

初始化过程

方法调用

如前文所述,在 React Native 中,Objective-C 和 JavaScript 的交互都是经过传递 ModuleIdMethodId 和 Arguments 进行的。如下是分状况讨论:

调用 JavaScript 代码

也许你在其余文章中曾经屡次据说 JavaScript 代码老是在一个单独的线程上面调用,它的实际含义是 Objective-C 会在单独的线程上运行 JavaScript 代码:

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
  if ([NSThread currentThread] != _javaScriptThread) {
    [self performSelector:@selector(executeBlockOnJavaScriptQueue:)
                 onThread:_javaScriptThread withObject:block waitUntilDone:NO];
  } else {
    block();
  }
}

调用 JavaScript 代码的核心代码以下:

- (void)_executeJSCall:(NSString *)method
             arguments:(NSArray *)arguments
              callback:(RCTJavaScriptCallback)onComplete{
    [self executeBlockOnJavaScriptQueue:^{
        // 获取 contextJSRef、methodJSRef、moduleJSRef
        resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, arguments.count, jsArgs, &errorJSRef);
        objcValue = /*resultJSRef 转换成 Objective-C 类型*/
        onComplete(objcValue, nil);
    }];
}

须要注意的是,这个函数名是咱们要调用 JavaScript 的中转函数名,好比 callFunctionReturnFlushedQueue。也就是说它的做用实际上是处理参数,而非真正要调用的 JavaScript 函数。

这个中转函数接收到的参数包含了 ModuleIdMethodId 和 Arguments,而后由中转函数查找本身的模块配置表,找到真正要调用的 JavaScript 函数。

在实际使用的时候,咱们能够这样发起对 JavaScript 的调用:

[_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                         body:@{ @"name": @"nmae"}];

这里的 Name 和 Body 参数分别表示要调用的 JavaScript 的函数名和参数。

JavaScript 调用 Objective-C

在调用 Objective-C 代码时,如前文所述,JavaScript 会解析出方法的 ModuleIdMethodId 和 Arguments 并放入到 MessageQueue 中,等待 Objective-C 主动拿走,或者超时后主动发送给 Objective-C。

Objective-C 负责处理调用的方法是 handleBuffer,它的参数是一个含有四个元素的数组,每一个元素也都是一个数组,分别存放了ModuleIdMethodIdParams,第四个元素目测用处不大。

函数内部在每一次方调用中调用 _handleRequestNumber:moduleID:methodID:params 方法。,经过查找模块配置表找出要调用的方法,并经过 runtime 动态的调用:

[method invokeWithBridge:self module:moduleData.instance arguments:params];

在这个方法中,有一个很关键的方法:processMethodSignature,它会根据 JavaScript 的 CallbackId 建立一个 Block,而且在调用完函数后执行这个 Block。

实战应用

俗话说:“思而不学则神棍”,下面举一个例子来演示 Objective-C 是如何与 JavaScript 进行交互的。首先新建一个模块:

// .h 文件
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"

@interface Person : NSObject<RCTBridgeModule, RCTBridgeMethod>

@end

Person 这个类是一个新的模块,它有两个方法暴露给 JavaScript:

#import "Person.h"
#import "RCTEventDispatcher.h"
#import "RCTConvert.h"

@implementation Person
@synthesize bridge = _bridge;

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(greet:(NSString *)name)
{
  NSLog(@"Hi, %@!", name);
  [_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                           body:@{ @"name": @"nmae"}];
}

RCT_EXPORT_METHOD(greetss:(NSString *)name name2:(NSString *)name2 callback:(RCTResponseSenderBlock)callback)
{
  NSLog(@"Hi, %@! %@!!!", name, name2);
  callback(@[@[@12,@23,@34]]);
}

@end

在 JavaScript 中,能够这样调用:

Person.greet('Tadeu');
Person.greetss('Haha', 'Heihei', (events) => {
  for (var i = 0; i < events.length; i++) {
    console.log(events[i]);
  }
});

有兴趣的同窗能够复制以上代码并自行调试。

React Native 优缺点分析

通过一长篇的讨论,其实 React Native 的优缺点已经不难分析了,这里简单总结一下:

优势

  1. 复用了 React 的思想,有利于前端开发者涉足移动端。
  2. 可以利用 JavaScript 动态更新的特性,快速迭代。
  3. 相比于原平生台,开发速度更快,相比于 Hybrid 框架,性能更好。

缺点

  1. 作不到 Write once, Run everywhere,也就是说开发者依然须要为 iOS 和 Android 平台提供两套不一样的代码,好比参考官方文档能够发现很多组件和API都区分了 Android 和 iOS 版本。即便是共用组件,也会有平台独享的函数。

  2. 不能作到彻底屏蔽 iOS 端或 Android 的细节,前端开发者必须对原平生台有所了解。加剧了学习成本。对于移动端开发者来讲,彻底不具有用 React Native 开发的能力。

  3. 因为 Objective-C 与 JavaScript 之间切换存在固定的时间开销,因此性能一定不及原生。好比目前的官方版本没法作到 UItableview(ListView) 的视图重用,由于滑动过程当中,视图重用须要在异步线程中执行,速度太慢。这也就致使随着 Cell 数量的增长,占用的内存也线性增长。

综上,我对 React Native 的定位是:

利用脚本语言进行原平生台开发的一次成功尝试,下降了前端开发者入门移动端的门槛,必定业务场景下具备独特的优点,几乎不可能取代原平生台开发。

参考资料

  1. React Native 官方文档React Native 官方文档中文版
  2. React Native通讯机制详解
  3. React 入门实例教程
相关文章
相关标签/搜索