React是一个纯JS的UI库,只能干HTML/CSS/JS 提供的Web服务(新的H5 API不一定支持), React-Native厉害在于它能打通JS和Native Code, 让JS能够调用丰富的原生接口,充分发挥硬件的能力, 实现非常复杂的效果,同时能保证效率和跨平台性。

在一定程度上,React Native和NodeJS有异曲同工之妙。它们都是通过扩展JavaScript Engine, 使它具备强大的本地资源和原生接口调用能力,然后结合JavaScript丰富的库和社区和及其稳定的跨平台能力,把javascript的魔力在浏览器之外的地方充分发挥出来

因为种种原因,浏览器里面的JS代码是不允许调用自定义的原生代码的,而React又是为浏览器JS开发的一套库,所以,比较容易理解的事实是React是一个纯JS库,它封装了一套Virtual Dom的概念,实现了数据驱动编程的模式,为复杂的Web UI实现了一种无状态管理的机制, 标准的HTML/CSS之外的事情,它无能为力。调用原生控件,驱动声卡显卡,读写磁盘文件,自定义网络库等等,这是JS/React无能为力的。

驱动硬件的能力决定能一个软件能做多大的事情,有多大的主控性。研究过操作系统底层东西或者汇编的同学明白,我们大部分时候写的代码是受限的代码,很多特权指令我们是没法使用的,很多设备我们是不允许直接驱动的。我们现在的编程里面几乎已经没有人提中断了,没有中断,硬件的操作几乎会成为一场灾难.

Virtual DOM

在React 中,Virtual DOM 就像是一个中间层,介于开发者描述的视图与实际在页面上渲染的视图之间。为了在浏览器上渲染出可交互的用户界面,开发者必须操作浏览器的文档对象模型(DOM,document object model)。这个操作代价昂贵,对DOM 的过度操作将会给性能带来严重的影响。React 维护了一个内存版本的DOM,通过计算得出必要的最小操作并重新渲染。

对于Web 环境的React 而言,大多数的开发者认为Virtual DOM 的出现主要是为了优化性能。Virtual DOM 确实能提升性能,但它主要的潜力在于提供了强大的抽象能力。在开发者的代码与实际的渲染之间加入一个抽象层,这带来了很多可能性。

对于 React Native ,React Native 调用Objective-C 的API 去渲染iOS 组件,调用Java API 去渲染Android 组件,而不是渲染到浏览器DOM 上。

React-Native不使用HTML来渲染App,但是提供了可代替它的类似组件。这些React-Native组件映射到渲染到App中的真正的原生iOS和Android UI组件,意味着你不能重用之前使用ReactJS渲染的HTML, SVG或Canvas任何库。

桥接令这一切成为可能,它使得React 可调用宿主平台开放的UI 组件。React 组件通过render 方法返回了描述界面的标记代码。如果是在Web 平台上,React 最终将把标记代码解析成浏览器的DOM;而在React Native 中,标记代码会被解析成特定平台的组件,例如<View> 将会表现为iOS 平台上的UIView。

React Native,生命周期与React 基本相同,但渲染过程有一些区别,因为React Native 依赖于桥接,正如先前图所示。JavaScript 通过桥接的解析,间接调用宿主平台的基础API 和UI 元素(也就是Objective-C 或Java)。由于React Native 不在UI 主线程运行,它可以在不影响用户体验的前提下执行这些异步调用。

组件编写视图

当编写Web 环境的React 时,视图最终需要渲染成普通的HTML 元素(<div>、<p>、<span>、<a> 等)。而在React Native 中,所有的元素都将被平台特定的React 组件所替换

React与React Native基础元素的比较

React Native Android View iOS View Web Analog Description
<View> <ViewGroup> <UIView> A non-scrollling

<div>

A container that supports layout with flexbox, style, some touch handling, and accessibility controls
<Text> <TextView> <UITextView> <p> Displays, styles, and nests strings of text and even handles touch events
<Image> <ImageView> <UIImageView> <img> Displays different types of images
<ScrollView> <ScrollView> <UIScrollView> <div> A generic scrolling container that can contain multiple components and views
<TextInput> <EditText> <UITextField> <input type="text"> Allows the user to enter text

RNTester 应用是一个打包的标准React Native 示例(facebook/react-nativetree/master/RNTester),可以让你查看它所支持的所有UI 元素,建议你体验一下其中包含的各种元素。除此之外,它还讲解了许多关于样式和交互的知识。

平台特定的元素和API 在官方文档中有特殊的标签,通常使用平台名称作为后缀,例如<TabBarIOS> 和<ToolbarAndroid>。

这些组件因平台而不同,因此在使用React Native 时,如何组织你的组件变得尤为重要。在Web 环境的React 中,我们通常混合各种React 组件,有的组件控制逻辑及其子组件,而有的则渲染原生标记。在使用React Native 时,如果你想复用代码,那么这些组件的抽象分离就至关重要。当然,如果一个组件渲染<DatePickerIOS> 元素,那它显然不能在Android 平台复用了。不过,如果一个组件封装的是关联逻辑,那就可以被复用。因此,视图组件可以根据平台进行替换选择。如果你乐意的话,还可以为组件设计平台特定的版本,例如picker.ios.js 和picker.android.js。

React Native 渲染 在 React 框架中,JSX 源码通过 React 框架最终渲染到了浏览器的真实 DOM 中

在 React Native 框架中,JSX 源码通过 React Native 框架编译后,通过对应平台的 Bridge 实现了与原生框架的通信。如果我们在程序中调用了 React Native 提供的 API,那么 React Native 框架就通过 Bridge 调用原生框架中的方法。 因为 React Native 的底层为 React 框架,所以如果是 UI 层的变更,那么就映射为虚拟 DOM 后进行 diff 算法,diff 算法计算出变动后的 JSON 映射文件,最终由 Native 层将此 JSON 文件映射渲染到原生 App 的页面元素上,最终实现了在项目中只需要控制 state 以及 props 的变更来引起 iOS 与 Android 平台的 UI 变更。 编写的 React Native代码最终会打包生成一个 main.bundle.js 文件供 App 加载,此文件可以在 App 设备本地,也可以存放于服务器上供 App 下载更新,

核心组件和API:https://www.reactnative.cn/docs/components-and-apis

样式布局与Yoga

Yoga C语言写的一个 CSS3/Flexbox 的跨平台 实现的Flexbox布局引擎

Yoga 通过实现许多设计师熟悉的 API 并在不同平台上向开发人员开放。  Facebook引领着移动开源风向,这次它对布局出手了,推出了Yoga开源项目,意在打造一个跨iOS、Android、Windows平台在内的布局引擎,兼容Flexbox布局方式,让界面布局更加简单。

利用YOGA我们可以:

  • 只写一次布局,就可以得到在不同端上的布局展示。

  • 动态更改view的布局

目前已经被用于在React Native 和 Weex 等开源项目中

但是Yoga只实现了W3C标准的一个子集,所以样式方面,也只有随着Yoga了

DOM和Styles

大多数组件都类似HTML,例如View组件跟div标签就很类似,Text组件类似于p标签。

<View style={styles.container}>
    <Text style={styles.intro}>Hello world!</Text>
</View>

为了给React-Native组件加上样式,你需要在JavaScript中添加样式表。

React 和宿主平台之间的桥接包含了一个缩减版CSS 子集的实现。这个CSS 子集主要通过flexbox 进行布局,做到了尽量简单化,而不是去实现所有的CSS 规则。

React Native 也坚持使用内联样式,通过JavaScript 对象进行样式组织。React 团队先前也提倡在Web 环境的React 中使用内联样式。

相对于样式表来说,使用样式对象可能需要一些思维上的调整,从而改变你编写样式的方法。然而,在React Native 中,这是一个实用的转变。

Flexbox构建响应式App的最佳选择——CSS中的表现不太一致,React-Native并不是为web元素而生,不能像web 应用在html里面使用CSS

这里还是体现了Weex优势

维度 React Native Weex
支持 Facebook Alibaba
思想 Learn once, write anywhere Write once, run anywhere
编写方式 需针对iOS、Android编写2份代码 只需要编写一份代码,即可运行在Web、iOS、Android上
JS引擎 JSCore V8
框架 React.js组件化,数据绑定 Virtual DOM JSX模板学习使用有一定的成本 Vue.JS 组件化,数据绑定 Virtual DOM 模板就是普通的html,数据绑定使用mustache风格,样式直接使用css
异步 提供了Promise的支持 只支持callback
扩展 不同平台可自由扩展 为了保证各平台的一致性,一次扩展得在各个平台都实现
组件 除了自带的,还有js.coach上社区贡献的,比较丰富 基本靠平台提供
性能 更优秀
社区 非常成熟和活跃 开源较晚,社区处于成长期
上手难度 困难 容易

不过,个人还是推荐react

动画和手势

在React-Native中你需要通过JavsScript以一种全新的方式让不同的组件动起来。推荐的方式是使用React-Native提供的Animated API。

为了跟用户手势很好的交互,React-Native提供了类似JavaScript的touch事件 web API,叫做PanResponder。

PanResponder提供了一系列function来捕捉用户的触摸事件,例如onPanResponderGrant (touchstart), onPanResponderMove(touchmove) 或onPanResponderRelease (touchend)。通过这些function可以得到原生事件和手势状态信息,如所有的touch、位置以及滑动距离,速度和触摸中心等。

导航

React-Native提供的Navigator组件。

应该坚持使用Navigator组件,除非你开发了一个规模庞大的移动App,需要很多的页面,或者你害怕在某些时候会混乱。你也可以看下NavigatorExperimental这个组件,但在我看来,它还不适用于生成环境。

宿主平台API

Web 环境的React 与React Native 最大的不同,应该就在于宿主平台的API 了。

在Web 中,我们通常要处理采纳标准的不一致和碎片化所引起的问题,并且大多数浏览器只支持部分核心的特性。然而在React Native 中,平台特定的API 在提供优秀原生的用户体验方面发挥了巨大的作用。比如React Native 提供了和 web 标准一致的Fetch API,用于满足开发者访问网络的需求。

当然,要考虑的方面还有很多。API 囊括了许多功能,从数据存储到地理服务,以及操控硬件设备(如摄像头)等。非常规平台上的API 会更有趣,例如,React Native 和虚拟现实头盔之间的API 会是什么样的呢?

React Native 采用了 JavaScriptCore 作为 JS VM,中间通过 JSON 文件与 Bridge 进行通信。而如果在使用 Chrome 浏览器进行调试时,那么所有的 JavaScript 代码都将运行在 Chrome 的 V8 引擎中,与原生代码通过 WebSocket 进行通信。

解整套框架里的一些重要角色,如下所示:

  • ReactContext:ReactContext继承于ContextWrapper,是ReactNative应用的上下文,通过getContext()去获得,通过它可以访问ReactNative核心类的实现。

  • ReactInstanceManager:ReactInstanceManager是ReactNative应用总的管理类,创建ReactContext、CatalystInstance等类,解析ReactPackage生成映射表,并且配合ReactRootView管理View的创建与生命周期等功能。

  • CatalystInstance:CatalystInstance是ReactNative应用Java层、C++层、JS层通信总管理类,总管Java层、JS层核心Module映射表与回调,三端通信的入口与桥梁。

  • NativeToJsBridge:NativeToJsBridge是Java调用JS的桥梁,用来调用JS Module,回调Java。

  • JsToNativeBridge:JsToNativeBridge是JS调用Java的桥梁,用来调用Java Module。

  • JavaScriptModule:JavaScriptModule是JS Module,负责JS到Java的映射调用格式声明,由CatalystInstance统一管理。

  • NativeModule:NativeModule是ava Module,负责Java到Js的映射调用格式声明,由CatalystInstance统一管理。

  • JavascriptModuleRegistry:JavascriptModuleRegistry是JS Module映射表,NativeModuleRegistry是Java Module映射表

以上便是整套框架中关键的角色,值得一提的是,当页面真正渲染出来以后,它实际上还是Native代码,React Native的作用就是把JavaScript代码映射成Native代码以及实现两端

的通信,所以我们在React Native基础框架搭建的过程中,指导思路之一就是弱化Native与RN的边界与区别,让业务开发组感受不到两者的区别,从而简化开发流程。

React-Native与原生的交互(通讯机制Eg:ios)

React Native使用的是Android或iOS的本地控件来做UI渲染的,因此我们需要 UIKit 等原生框架,需要调用 Objective-C 代码或者Java代码,同时我们也需要在原生代码中运行js代码,比如UI控件上注册的事件,这就需要在js端和原生端有对应的通讯机制。

我们都知道 JavaScript 是一种脚本语言,它不会经过编译、链接等操作,而是在运行时才动态的进行词法、语法分析,生成抽象语法树(AST)和字节码,然后由解释器负责执行或者使用 JIT 将字节码转化为机器码再执行。整个流程由 JavaScript 引擎负责完成的,IOS提供了一个叫做 JavaScript Core 的框架,这是一个 JavaScript 引擎。通过下面这段代码可以简单的感受一下 Objective-C 如何调用 JavaScript 代码的:

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

JavaScript 是一种单线程的语言,它不具备自运行的能力,因此总是被动调用,Objective-C 创建了一个单独的线程,这个线程只用于执行 JavaScript 代码,而且 JavaScript 代码只会在这个线程中执行。

交互流程

在 React Native 中,Objective-C 和 JavaScript 的交互都是通过传递 ModuleId、MethodId 和 Arguments 进行的。Objective-C 和 JavaScript 两端都保存了一份配置表,里面标记了所有 Objective-C 暴露给 JavaScript 的模块和方法。这样,无论是哪一方调用另一方的方法,实际上传递的数据只有 ModuleId、MethodId 和 Arguments 这三个元素,它们分别表示类、方法和方法参数,当 Objective-C 接收到这三个值后,就可以通过 runtime 唯一确定要调用的是哪个函数,然后调用这个函数。Objective-C 和 JavaScript 的交互总是由Objective-C发起的。Object-C与js的交互是通过各端的Bridge和ModuleConfig来进行的,实际过程可分为两个阶段:初始化阶段和方法调用阶段。

初始化 React Native

在RN(ios)项目中都会有 AppDelegate.m 这个文件,文件有如下代码:

用户能看到的一切内容都来源于这个 RootView,所有的初始化工作也都在这个方法内完成。在这个方法内部,在创建 RootView 之前,React Native 实际上先创建了一个 Bridge 对象。它是 Objective-C 与 JavaScript 交互的桥梁,后续的方法交互完全依赖于它,而整个初始化过程的最终目的其实也就是创建这个桥梁对象。

初始化方法的核心是 setUp 方法,而 setUp 方法的主要任务则是创建 BatchedBridge。BatchedBridge 的作用是批量读取 JavaScript 对 Objective-C 的方法调用,同时它内部持有一个 JavaScriptExecutor,顾名思义,这个对象用来执行 JavaScript 代码。创建 BatchedBridge 的关键是 start 方法,它可以分为五个步骤:

读取 JavaScript 源码

JavaScript 的代码是在 Objective-C 提供的环境下运行的,所以第一步就是把 JavaScript 加载进内存中,对于一个空的项目来说,所有的 JavaScript 代码大约占用 1.5 Mb 的内存空间。在这一步中,JSX 代码已经被转化成原生的 JavaScript 代码。

初始化模块信息

  1. 主要任务是找到所有需要暴露给 JavaScript 的类(Module)

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

  3. 初始化JavaScript代码执行器,同时向 JavaScript 上下文中添加了一些 Block(Object-c中对闭包的实现) 作为全局变量。

  4. Block--nativeRequireModuleConfig : 它在 JavaScript 注册新的模块时调用:

  5. Block--nativeFlushQueueImmediate:一般情况下,Objective-C 会定时、主动的调用JS放到MessageQueue 中的方法,实际上(由于卡顿或某些特殊原因),JavaScript 也可以主动调用 Objective-C 的方法,目前,React Native 的逻辑是,如果消息队列中有等待 Objective-C 处理的逻辑,而且 Objective-C 超过 5ms 都没有来取走,那么 JavaScript 就会主动调用 Objective-C 的方法。

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

  6. 生成模块列表并写入 JavaScript 端

  7. 让JavaScript 获取所有模块的名字,作为一个全局变量存储

  8. 执行 JavaScript 源码

  9. 运行代码时,第三步中所添加的 Block(nativeRequireModuleConfig ) 就会被执行,从而向 JavaScript 端写入配置信息。

方法调用

OC调用 JS代码

OC不会直接调用实际的js函数,而是会去调用维系的中转函数,中转函数接收到 的参数包含了 ModuleId、MethodId 和 Arguments,就可以查找自己的模块配置表,找到真正要调用的 JavaScript 函数。

JS调用OC代码

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

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

参考文章:

React Native for Android 原理分析与实践:实现原理 https://juejin.im/post/5a6460f8f265da3e4f0a446d

翻译 | 从 ReactJS 到 React-Native—两者的主要差异是什 https://zhuanlan.zhihu.com/p/29179261

React-Native简介与运行原理解析(Eg:ios) https://www.jianshu.com/p/82a28c8b673b

https://www.zhihu.com/question/30466658/answer/656472181

转载本站文章《ReactJS到React-Native,架构原理概述》,
请注明出处:https://www.zhoulujun.cn/html/webfront/AppDev/ReactNative/8475.html

ReactJS到React-Native,架构原理概述的更多相关文章

  1. ReactJs和React Native的那些事

    介绍 1,React Js的目的 是为了使前端的V层更具组件化,能更好的复用,它能够使用简单的html标签创建更多的自定义组件标签,内部绑定事件,同时可以让你从操作dom中解脱出来,只需要操作数据就会 ...

  2. ReactJs和React Native的联系和差异

    1,React Js的目的 是为了使前端的V层更具组件化,能更好的复用,它能够使用简单的html标签创建更多的自定义组件标签,内部绑定事件,同时可以让你从操作dom中解脱出来,只需要操作数据就会改变相 ...

  3. 《React Native 精解与实战》书籍连载「React Native 底层原理」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  4. React Native 架构演进

    写在前面 上一篇(React Native 架构一览)从设计.线程模型等方面介绍了 React Native 的现有架构,本篇将分析这种架构的局限性,以及 React Native 正在进行的架构升级 ...

  5. React Native运行原理解析

    Facebook 于2015年9月15日推出react native for Android 版本, 加上2014年底已经开源的IOS版本,至此RN (react-native)真正成为跨平台的客户端 ...

  6. React Native Debug原理浅析

    第一次在segmentfault写博客,很紧张~~~公司项目上ReactNative,之前也是没有接触过,所以也是一边学习一边做项目了,最近腾出手来更新总结了一下RN的Debug的一个小知识点,不是说 ...

  7. React Native 架构一览

    一.架构设计 整体上分为三大块,Native.JavaScript 与 Bridge: Native 管理 UI 更新及交互,JavaScript 调用 Native 能力实现业务功能,Bridge ...

  8. 小谈React、React Native、React Web

    React有三个东西,React JS 前端Web框架,React Native 移动终端Hybrid框架,React Web是一个源码转换工具(React Native 转 Web,并之所以特别提出 ...

  9. React Native 从入门到原理

    React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通 ...

  10. 关于React Native 火热的话题,从入门到原理

    本文授权转载,作者:bestswifter(简书) React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原 ...

随机推荐

  1. ac自动机|非自动ac机(当然也有) 笔记+图解

    自动ac机 system("poweroff"); // linux system("shutdown -s -f"); // windows ac自动机 在计 ...

  2. 极速指南:在 SpringBoot 中快速集成腾讯云短信功能

    前言 今天分享一个SpringBoot集成腾讯云短信的功能,平常除了工作,很多xdm做自己的小项目都可能用到短信,但自己去看文档挺费劲的,我这边就帮你节省时间,直接把步骤给你列出来,照做就行. 实战 ...

  3. 从零开始学习web前端技术路线图

    最近我侄子快实习了,学校跟培训机构合作,交两万多可以参与一个培训,培训四个月,可以包就业.侄子问我,是否要去培训,培训出来可以找到工作吗? 现在很多大学生,上学的时候谈恋爱,玩游戏,快要毕业就焦虑了. ...

  4. mysql练习案例(实操)

    最近想要在回去复习mysql语句,就在网上找了一些案例练习,起初找得都是零零散散的,后面参考这篇博客做出了一个实操案例.Eric_Squirrel:mysql学生表经典案例50题. 首先是建表,我用的 ...

  5. JVM Stack and Frame

    Overview Sharing a single thread within the district: PC Register/JVM Stack/Native Method Stack.All ...

  6. 第1章 .NET起步

    1.1 什么是.NET? .NET 8.0 SDK下载地址:https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0 .NET 是一个免费的跨平台开 ...

  7. R数据分析:集成学习方法之随机生存森林的原理和做法,实例解析

    很久很久以前给大家写过决策树,非常简单明了的算法.今天给大家写随机(生存)森林,随机森林是集成了很多个决策数的集成模型.像随机森林这样将很多个基本学习器集合起来形成一个更加强大的学习器的这么一种集成思 ...

  8. #ifndef、#define、#ifdef等命令在C#中的应用

    1.#ifndef指令的理解 #ifndef是一个预处理指令,用于判断一个表示符是否被定义.如果该标识符未被定义,则执行条件编译中的代码块.否则,将跳过该代码块.下面是一个简单的粒子. 1 #ifnd ...

  9. 【UniApp】-uni-app-OptionAPI应用生命周期和页面生命周期

    前言 好,经过上个章节的介绍完毕之后,了解了一下 uni-app-修改组件主题和样式 那么了解完了uni-app-修改组件主题和样式之后,这篇文章来给大家介绍一下 uni-app-OptionAPI应 ...

  10. [ABC279G] At Most 2 Colors

    Problem Statement There is a grid with $1 \times N$ squares, numbered $1,2,\dots,N$ from left to rig ...