RN iOS开发相关

React Native 从入门到原理

2017-11-28  本文已影响21人  攻克乃还_

React

我想动态修改一个按钮的文字,需要这样写:

<button type="button" id="button" onclick="onClick()">old button</button>
// 在 JavaScript 中操作 DOM:
<script>
function onClick() {
  document.getElementById('button').innerHTML='new button';
}
</script>

可以看到,在 HTML 和 JavaScript 代码中,id 和 onclick 事件触发的函数必须完全对应,否则就无法正确的响应事件。如果想知道一个 HTML 标签会如何被响应,我们还得跑去 JavaScript 代码中查找,这种原始的配置方式很不方便。

于是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 是一套可以用 简洁 的语法 高效 绘制 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

移动端通过 JSON 文件传递信息,只能传递配置信息,无法表达逻辑。

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

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

这是一个面向前端开发者的框架。它的宗旨是让前端开发者像用 React 写网页那样,用 React Native 写移动端应用。

原理概述

// Objective-C 如何调用 JavaScript :
JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];

Objective-C 与 JavaScript 交互

Objective-C 和 JavaScript 的交互总是由前者发起

由于 JavaScript Core 是一个面向 Objective-C 的框架,在 Objective-C 这一端,我们对 JavaScript 上下文知根知底,可以很容易的获取到对象,方法等各种信息,当然也包括调用 JavaScript 函数。

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

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

React Native Objective-C端源码分析

配置表的形成 (Objective-C 调用 JavaScript)

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

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

用户能看到的一切内容都来源于这个 rootView,所有的初始化工作也都在这个方法内完成。

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

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

BatchedBridge对象的作用是批量读取 JavaScript 对 Objective-C 的方法调用,同时它内部持有一个 RCTJSCExecutor对象 对象,用来执行 JavaScript 代码。

创建 BatchedBridge对象 的关键是 start 方法,start方法又分为五个步骤:

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

方法调用

如前文所述,在 React Native 中,Objective-C 和 JavaScript 的交互都是通过传递 ModuleId、MethodId 和 Arguments 进行的。以下是分情况讨论:

调用 JavaScript 方法

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

// 这个函数是我们要调用 JavaScript 的中转函数。也就是说它的作用其实是处理参数,而非真正要调用的 JavaScript 函数。
// 这个中转函数接收到的参数包含了 ModuleId、MethodId 和 Arguments,然后由中转函数查找自己的模块配置表,找到真正要调用的 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 的调用:

// Name 和 Body 参数分别表示要调用的 JavaScript 的函数名和参数
[_bridge.eventDispatcher sendAppEventWithName:@"greeted"
                                         body:@{ @"name": @"nmae"}];

调用 Objective-C方法

JavaScript闭包的回调

既然说到函数互调,那么就不得不提到回调了。对于 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 并执行

实战举例

// .h 文件
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
@interface Person : NSObject<RCTBridgeModule, RCTBridgeMethod>
@end

// .m 文件
#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 优缺点

优点:

1.复用了 React 的思想,有利于前端开发者涉足移动端

2.能够利用 JavaScript 动态更新的特性,快速迭代

3.相比于原生平台,开发速度更快,相比于 Hybrid 框架,性能更好

缺点

1. 开发者依然要为 iOS 和 Android 两个平台提供两套不同的代。有组件是区分平台的,即使是共用组件,也会有平台独享的函数。

2.  不能做到完全屏蔽 iOS 端或 Android端,前端开发者必须对原生平台有所了解。

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

React Native 交互原理总结

Objective-C 有 `JavaScript Core` 框架用来执行 JavaScript 代码。
JavaScript 通过配置表生成类,方法,参数三个元素,放入消息队列,Objective-C获取之后,
就可以唯一确定要调用的是哪个Objective-C函数,然后调用

参考资料
React Native 从入门到原理-bestswifter

上一篇下一篇

猜你喜欢

热点阅读