0
点赞
收藏
分享

微信扫一扫

你所不了解的React Native


1.1 React Native基本知识

React Native基于React框架而设计,因此,了解React有助于我们更好地理解React Native。

1.1.1 React简介

React是由Facebook推出的前端开发框架,其本身作为MVC模式中的View层来构建UI,也可以以插件的形式应用到Web应用程序的非UI部分构建中,轻松实现与其他JS框架的整合。同时,React通过对虚拟DOM的操作来控制真实的DOM,从而得到页面的局部更新,提高了GPU渲染的性能,而React提出的模块化开发思路也大大提高了代码的可维护性。

React的官方地址是​​https://github.com/facebook/react​​。截至2017年4月,React获得了超过62K的star和11K的fork,这说明React得到了技术人员的普遍支持,正是由于这些原因,React.js和Vue.js、Angular.js成为了当今最流行的三大前端框架。

讲到React,就不得不提到组件(Component)的概念,它是React最基础的部分,其功能相当于AngularJS里面的Directive,或是其他JS框架里面的Widgets或Modules。Component可以认为是由HTML、CSS、JavaScript和一些内部数据组合而成的模块,当然Component也可以由很多其他的Component组建而成。不同的Component既可以用纯JavaScript定义,也可以用特有的JavaScript语法JSX创建而成。采用React进行项目开发,能够获得以下优势。

虚拟DOM(Virtual DOM)

传统的Web应用开发,一般都是通过直接操作真实DOM来进行更新操作的,如图1-1所示,但对DOM进行操作通常是比较昂贵的。而React为了尽可能减少对真实DOM的操作,采用了一种强大的方式来更新DOM,代替直接的DOM操作,这就是Virtual DOM,一个轻量级的虚拟DOM。

图1-1 传统的Web应用结构图

虚拟DOM其实是React抽象出一个对象,通过这个Virtual DOM可以更新真实的DOM,由这个Virtual DOM管理真实DOM的更新,如图1-2所示。简单来说,React在每次需要渲染时,会先比较当前DOM内容和待渲染内容的差异,然后再决定如何最优地更新DOM,这个过程被称为reconciliation。

图1-2 React Web应用结构图

除了性能方面的考虑,React引入虚拟DOM更重要的意义在于提供了一种新的开发方式来开发服务端应用、Web应用和手机端应用,如图1-3所示。

图1-3 虚拟DOM结构图

Components组件

虚拟DOM(virtual-dom)不仅带来了简单的UI开发逻辑,同时也带来了组件化开发的思想。所谓组件,即自己封装的具有独立功能的UI部件。React推荐以组件的方式去构成视图,并建议将功能相对独立的模块抽象为组件。例如,Facebook的instagram.com网站都采用React来开发,整个页面就是一个大的组件。

对于React而言,界面被分成不同的组件,每个组件都相对独立。在React开发中,整个界面可以看成是由大小组件构成,每个组件实现自己的逻辑部分即可,彼此独立,如图1-4所示。

采用组件化开发,往往具有以下特点:

  • 可组合(Composeable):一个组件易于和其他组件一起使用,或者嵌套在另一个组件内部。如果在一个组件内部创建了另一个组件,那么父组件拥有它创建的子组件,通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件。
  • 可重用(Reusable):每个组件都可以独立出来,被使用在其他相似的UI场景中。
  • 可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护。
  • 可测试(Testable):每个组件都是独立的,那么对于各个组件分开测试显然要比整个界面容易得多。

图1-4 Components组件示意图

数据流(Data Flow)

React采用单向的数据流,即从父节点到子节点的传递,因此更加灵活便捷,也提高了代码的可控性。React单向数据流可以总结为以下流程:Action→Dispatcher→Stroe→View,如图1-5所示。

图1-5 React单向数据流流程图

JSX语法

JSX是React的核心组成部分,React使用JSX来替代常规的JavaScript。它使用XML标记的方式去直接声明界面,目的是通过各种不同的编译器将这些标记编译成标准的JS语言。使用JSX语法后,可以让组件的结构和组件之间的关系看上去更加清晰并且执行效率更高。

1.1.2 React Native简介

React Native(简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的UI框架React在原生移动应用平台的衍生产物,目前支持iOS和Android两大平台。

React Native可以基于目前大热的开源JavaScript库ReactJS来开发iOS和Android移动应用,因为往往只需要开发一套代码就可以满足iOS和Android,正如Facebook说的“Learn once, write anywhere”(仅需学习一次,编写任何平台),由于基于Web技术,React Natie开发起来可以像在浏览器那样随改即所见。

React Native的原理是在JavaScript中使用React抽象操作系统的原生UI组件,代替DOM元素来渲染界面,比如用<View>组件取代<div>,用<Image>组件替代<img>等。React Native主要运行着两个线程:主线程和JavaScript引擎线程,两个线程之间通过批量化的async消息协议来通信。

在UI方面,React Native提供跨平台的类似Flexbox的布局系统,还支持CSS子集。可以用JSX或者普通JavaScript语言,还有CoffeeScript和TypeScript来开发。运用React Native,我们可以使用同一份业务逻辑核心代码来创建原生应用运行在Web端、Android端和iOS端。

React Native相比Web开发或原生开发主要有以下特点:

APP占用体积小

随着业务的迭代,原生APP的体积越来越大,特别是一些老的APP,动辄上百兆,而采用React Native之后,占用体积会大大缩小,而且引入React Native可以实现持续开发。

实现跨平台开发

基于Web技术(HTML5/JavaScript)构建的移动应用速度快,开发周期短,但是体验较差,响应不及时;而使用原生开发,周期长,项目风险不可控。如何提高开发效率,节约人力成本。成为各大公司考虑的问题,而React Native的出现解决了上面的问题,只需要开发一套代码,便可以同时部署到Android和iOS两个移动平台上。

相对成熟的技术

随着Android/iOS的React Native陆续开源,原生提供的组件和API相对丰富,且实现技术基本一致,对于熟悉前端和原生APP开发的人员来说很容易上手。而React Native通过JavaScriptCore将JS转换为原生APP组件进行渲染,其用户体验也可媲美原生APP。

支持动态更新

在原生APP开发中,Android平台可以通过插件化实现热更新。在iOS平台上,热更新策略是严令禁止的(如JSPatch/wax/rollout等技术),而采用React Native技术完全可以满足要求,而又不触碰苹果的底线。

1.1.3 React Native工作原理

使用JavaScript开发移动APP的想法来源于最近几年市场对于移动应用需求的增长,为了快速开发一款可以使用,而体验又不是那么糟糕的APP,很多公司投入大量人力开发跨平台应用。而在这些公司当中,Facebook无疑是做得最好、最成功的。

为了更好地理解React Native,我们需要对React Native的工作原理和整体架构有一个了解,如图1-6所示。

图1-6 React Native整体框架图

如图,React Naitve框架分为3层,分别为:JSX环境层、虚拟DOM层、具体的平台层。这里面最重要的就是虚拟DOM层。

在React中,Virtual DOM就像一个中间虚拟层,位于JavaScript和实际渲染页面之间。对于JS开发者来说,只需要专注于UI层的绘制即可,不需要特别关心具体平台的实现。

虚拟DOM是一个 JavaScript 的树形结构,主要包含React元素和模块。组件的DOM结构就是映射到对应的虚拟DOM上,React通过渲染虚拟DOM到浏览器,使得用户界面得以显示。React之所以更新界面高效,是因为React的虚拟DOM采用了batching(批处理)和高效的Diff算法,采用Diff算法,可以将时间复杂度从O(n^3)降到O(n),从而提高界面构建的性能。当需要更新组件的时候,会通过Diff算法寻找到要变更的DOM节点,然后通知浏览器更新变化的内容。

虚拟DOM更新视图的过程可以总结为:状态变化→计算差异(Diff算法)→界面渲染。其渲染的原理如图1-7所示。

图1-7 虚拟DOM更新原理图

在界面渲染过程中,React Native针对不同的平台调用原生API去渲染界面,例如iOS平台调用其原生的API去渲染iOS界面,Android平台调用其原生API去渲染Android界面,而不是直接渲染到浏览器的DOM上,这使得React Native不同于基于Web视图的跨平台应用开发方案,因而,采用React Native开发的APP,体验更加接近原生APP。

虚拟DOM和MVVM的对比

虚拟DOM只是MVVM框架的一种实现方案,二者没有好坏之分。在流行的前端框架中,除了React采用虚拟DOM之外,其他MVVM系框架,如Angular、Vue、Avalon,采用的都是数据绑定。

何为数据绑定?简单来说,就是通过观察Directive/Binding对象数据变化,并保留对实际DOM元素的引用,当有数据变化时进行对应的操作。React检查是DOM结构层面的,而MVVM的检查则是数据层面的。MVVM的性能检测也根据检测层面的不同而有所不同:Angular的脏检查使得任何变动都会产生固定的更新的代价;而Vue/Avalon采用的依赖收集,使得在JS和DOM层面都会产生更新。

上面提到两个概念—脏检查和依赖收集。

  • 脏检查:scope digest +必要DOM更新
  • 依赖收集:重新收集依赖+必要DOM更新

可以看出,Angular效率低的地方在于任何小变动都会引起界面的重绘,但是,当所有数据都变化的时候,Angular并不吃亏。依赖收集在初始化和数据变化的时候都需要重新收集依赖,在数据流比较小的时候几乎可以忽略,但在数据量比较大的时候就会产生一定的消耗。相比之下,React的变动检查则是DOM结构层面的,即使是全新的数据,只要渲染结果没有变化,也不需要重新绘制。

Angular和Vue都提供了重绘的优化机制,即有效地复用实例和DOM元素。在优化的版本中,Angular和Vue采用了track by $index技术后比React的效率更高。

所以在框架选择和技术性能分析的时候,要分清楚初始渲染、小量数据更新、大量数据更新这些不同的场合,以及DOM、脏检查MVVM、数据收集MVVM在不同场合各自的表现和优缺点,具体表现和区别如下。

  • 初始渲染阶段:Virtual DOM >脏检查≥依赖收集
  • 小量数据更新时:依赖收集>Virtual DOM +优化>脏检查(无法优化)> Virtual DOM无优化
  • 大量数据更新时:脏检查+优化≥依赖收集+优化>Virtual DOM(无优化)> MVVM无优化

1.2 React Native与其他跨平台技术的对比优势

曾经大部分开发者以为可以通过Web技术来实现跨平台移动开发,却因为性能限制或其他问题而放弃,最终,不得不针对多个平台开发多个版本,这违背了跨平台开发的初衷。而React Native的出现让跨平台移动端开发再次回到人们的视野中,而它提倡的“Learn once, write anywhere”也赢得了广大开发人员的青睐。相比传统的H5技术,React Native获得了更加接近原生应用的体验。

为了方便理解,笔者将跨平台技术分为四大流派。

  • Web流:也被称为Hybrid技术,它基于Web相关技术来实现界面及功能。
  • 代码转换流:将某个语言转成Objective-C、Java或C#,然后使用不同平台下的官方工具来开发。
  • 编译流:将某个语言编译为二进制文件,生成动态库或打包成apk/ipa/xap文件。
  • 虚拟机流:通过将某个语言的虚拟机移植到不同的平台上来运行。

1.2.1 Web流

Web流,如大家熟知的PhoneGap/Cordova等技术,它将原生的接口封装后暴露给JavaScript,然后通过系统自带的WebView运行,也可以使自己内嵌Chrome内核。

Web流缺点是性能差、渲染速度慢。说它Web性能差,主要说的是在Android下比较差,在iOS下已经很流畅了。

性能差的主要原因是,在Android和iOS的早期设备中,由于没有实现GPU加速,所以会造成每次重绘界面的卡顿。

而造成渲染慢的第二个原因是:CSS过于复杂。因为从实现原理上看,Chrome和Android View并没有本质上的差别,但过于复杂的CSS会加重GPU的负担。那是不是可以通过简化CSS来解决呢?实际上还真有人进行了这种尝试,比如著名的Famo.us,其最大的特色就是不使用CSS,只能使用固定的几种布局方法,完全依靠JavaScript来写界面,它能有效避免低效的CSS代码,从而提升机器性能。

造成绘制缓慢的第三个原因是,业务需求的复杂,比如超长的ListView商品展示。因为DOM是一个很上层的API,使得JavaScript无法做到像Native那样细粒度地控制内存及线程,所以难以进行优化,特别是在硬件较差的机器上。

上面三个问题现在都不好解决。其实除了性能之外,Web流更严重的问题是功能缺失。比如iOS 8就新增4000多个API,而Web标准需要漫长的编写和评审过程,而等到Web审核通过,即便是Cordova这样的优秀的框架,或者自己封装也是忙不过来的。所以为了更好地使用原生系统新功能,Native是最快的选择。

1.2.2 代码转换流

不同平台下的官方语言不一样,并且平台对官方语言的支持最好,这就导致对于同样的逻辑,我们需要写多套代码。比如Android平台用Java,iOS用Objective-C或者Swift。于是就有人想到了通过代码转换的方式来减少重复的工作量,这就是代码转换流。

这种方式虽然听起来不是很靠谱,但它的成本和风险都是最小的,因为代码转换后就可以用官方提供的各种工具了,和普通开发区别不大,而且转换后,利用原生的优势,可以减少兼容性问题。

目前存在以下几种代码转换方式。

将Java转成Objective-C

2objc是一款能将Java代码转成Objective-C的工具,据说Google内部就是使用它来降低跨平台开发成本的,比如Google Inbox项目就号称通过它共用了70%的代码,效果很显著。有了2objc,我们就可以先开发Android版本,然后再开发iOS版本。

将Objective-C转成Java

MyAPPConverter是一款将Objective-C代码转换成Java代码的工具,比起前面的2objc,MyAPPConverter还打算将UI部分也包含进来,从它已转换的列表中可以看到还有UIKit、CoreGraphics等组件,使得有些应用可以不改代码就能转换成功。

XMLVM

除了上面提到的源码到源码的转换,在代码转换流中,还有XMLVM这种与众不同的转换方式,它首先将字节码转成一种基于XML的中间格式,然后再通过XSL来生成不同语言,目前支持生成C、Objective-C、JavaScript、C#、Python和Java。

虽然基于中间字节码可以支持多语言,但是这种方式也有一些问题,例如生成代码不可读,因为很多语言中的语法会在字节码中被抹掉,并且是不可逆的,所以不利于代码的调试和发现问题。

综上所述,虽然代码转换这种方式风险小,但对于很多小APP来说其实共享不了多少代码,因为这类应用大多数围绕业务来开发的,大部分代码都和业务逻辑耦合,所以公共部分不多,其意义不大。

1.2.3 编译流

编译流比代码转换流的代码转换更进一步,它直接将某个语言编译为普通平台下能够识别的二进制文件。采用这种方式主要有以下特点。

优点

  • 可以重用一些实现很复杂的代码(比如之前用C++实现的游戏引擎,重写一遍的成本太高)。
  • 编译后的代码反编译困难,安全性更好。

缺点

  • 转换过于复杂,并且后期定位和修改成本会很高。
  • 编译后体积太大,尤其是支持ARMv8和x86等CPU架构的时候。

常用的编译流方案如下所示。

C++方案

因为目前Android、iOS和Windows Phone都提供了对C++开发的支持。特别是C++在实现非界面部分,性能是非常高的。而如果使用C++实现非界面部分,还是比较有挑战的。这主要是因为Android程序的界面绝大部分是Java编写的,而在iOS和Windows Phone平台下可以分别使用C++的超集Objective-C和C++/C#来开发。要解决使用C++开发Android应用程序界面的问题,目前主要有两种方案。

  • 通过JNI调用系统提供的Java方法。
  • 自己实现UI部分。

第一种方式虽然可行,但是代码冗余高,实现过于复杂。那第二种方式呢,比如JUCE和Qt就是用代码实现的。不过在Qt的方案中,Android 5版本或更高版本环境下,很多效果都没法实现,比如按钮没有涟漪效果。根本原因在于它是通过Qt QUIck Controls的自定义样式来模拟的,而不是使用系统UI组件,因此它享受不到系统升级自动带来的界面优化。

当然我们可以使用OpenGL来绘制界面,因为EGL+OpenGL本身就是跨平台的。并且目前大多数跨平台游戏底层都是这么做的。

既然可以基于OpenGL来开发跨平台游戏,那么,是否能用它来进行界面开发呢?当然是可行的,而且Android 4的界面就是基于OpenGL的,不过它并不是只用OpenGL的API,那样是不现实的,因为OpenGL API最初设计并不是为了实现2D界面,所以连画个圆形都没有直接的方法,因此Android 4中是通过Skia将路径转换为位置数组或纹理,然后再交给OpenGL从而完成界面渲染的。

然而直接使用OpenGL绘制界面,不仅实现的代价大,而且目前支持的平台少。因此对于大多数应用来说自己实现界面是很不划算的。

Xamarin

Xamarin是从Mono发展而来,它用C#来开发Android及iOS应用,因为相关工具及文档都挺健全,因而发展得还不错。在UI界面方面,它可以通过调用系统API来使用系统内置的界面组件,或者基于Xamarin.Forms开发定制要求不高的跨平台UI。

从实现的方式来讲,iOS下是以AOT的方式编译为二进制文件的;而在Android平台上是通过内嵌的Mono虚拟机来实现,所以Xamarin是跨平台开发的不错选择。

对于熟悉C#的团队来说,Xamarin是一个很不错的方案,但这种方案最大的问题就是相关资料不足,遇到问题很可能搜不到解决方案,并且当前第三方库太少,加之Xamarin本身有些bug,所以让我们静待Xamarin做得更好吧。

Go

Go做为后端服务开发语言,专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++程序的速度,而且更加安全、支持并行进程。Go从1.4版本开始支持开发Android应用(1.5版本支持iOS)。虽然能同时支持Android和iOS,但是目前可用的API很少,Go语言仍然专注于后端开发。

目前,Android的View层完全是基于Java写的,要想用Go来完成界面的开发不可避免要调用Java代码,而在这方面Go还没有简便的实现方式,目前Go调用外部代码只能使用Cgo,通过Cgo再调用jni,这就不可避免地需要写很多的中间件。而且Cgo的实现本身就对性能有损失,除了各种无关函数的调用,它还会锁定一个Go的系统线程,这会影响其他gorountine的运行,如果同时运行太多外部调用,甚至会导致所有go线程处于等待状态。

所以,目前使用Go开发跨平台移动端应用并不靠谱。

1.2.4 虚拟机流

编译流是将代码编译为不同平台下的二进制文件,而另一种更彻底的做法是:通过虚拟机来支持跨平台运行,比如JavaScript和Lua都是天生的内嵌语言。不过使用虚拟机进行跨平台开发最普遍的两个问题是:性能损耗;虚拟机本身也会占据不小的空间。

Java虚拟机

说到虚拟机,大家肯定首先想到的是Java,因为Java一开始就是为跨平台而设计的,Sun的J2ME早在1998年就有了,在iPhone手机出来之前,很多小游戏都是基于J2ME开发的。前几年,微软为了支持移动端项目的发展,提供了一套将Android和iOS代码快速转移到Windows Phone的工具,不过后来不了了之。

前面提到C#和Java在iOS端的方案都是通过AOT的方式实现的,目前还没见到有Java虚拟机相应的方案,主要原因是iOS方面的限制。

Titanium/Hyperloop

Titanium和PhoneGap几乎是同时期的著名跨平台方案,和PhoneGap最大的区别是:它的界面没有使用HTML/CSS,而是自己设计了一套基于XML的界面框架Alloy。Titanium的代码风格如下:

APP/styles/index.tss
".container": {
backgroundColor:"white"
},
// This is APPlied to all Labels in the view
"Label": {
width: Ti.UI.SIZE,
height: Ti.UI.SIZE,
color: "#000", // black
transform: Alloy.Globals.rotateLeft // value is defined in the alloy.JS file
},
// This is only APPlied to an element with the id attribute assigned to "label"
"#label": {
color: "#999" /* gray */
}

虽然学习成本低,但Titanium同样面临着其他跨平台框架都存在的挑战:缺乏第三方库支持、对外的API较少。Titanium也意识到了这个问题,所以目前在开发下一代的解决方案Hyperloop,它可以将JavaScript编译为原生代码,这样开发者可以方便地调用原生API。比如调用iOS的写法如下:

@import("UIKit");
@import("CoreGraphics");
var view = new UIView();
view.frame = CGRectMake(0, 0, 100, 100);

这个方案和之前讲到的Xamarin如出一辙,也是将JavaScript翻译为Objective-C,然后交由官方系统运行。不过这个项目已经开发了快三年了,但至今仍然是试验阶段,笔者不建议尝试。

React Native

React Native是由Facebook开源的基于JavaScript和React搭建的一套跨平台开发框架。在设计之初,React Native采用的方案就是在不同平台下使用平台自带的UI组件来完成界面的绘制,再加上它采用JavaScript和React等前端语言来开发,所以获得了不少前端程序员的青睐。

有人说,React Native采用JS等前端技术来开发移动APP是回归H5,但其实React Native和Web扯不上太多关系,React Native虽然借鉴CSS中的Flexbox、navigator、XMLHttpRequest等API的写法,但是大部分还是通过原生的组件或者自己封装的组件来开发的。就像Facebook的内部软件Facebook Groups,iOS版本很大一部分基于React Native开发,其中用到了不少内部通用组件。

React Native相比传统原生开发,学习成本还是比较低的,熟悉JavaScript的开发者可以迅速实现界面,而使用标签加CSS样式表方式绘制的界面,远比原生使用代码绘制的界面更加易读,并且一套界面同时满足Android和iOS平台,这对于讨厌绘制界面的开发者来说是多么的诱惑。再加上React Native师出名门,截至目前,React Native已更新到0.4.4版本,并且趋于稳定。由于其更加接近原生的体验,国内一些大厂纷纷加入,诸如阿里、腾讯、美团等纷纷开始使用React Native改造一些应用型APP。

所以,不管是对于个人还是团队,现在跨平台开发做得最好的就是React Native,并且随着开源力量的加入,React Native会发展得越来越好。

1.3 小结

使用React Native开发跨平台移动APP是一个令人振奋的事情,相比其他跨平台方案,React Native在不牺牲用户体验和应用质量的前提下,提高了开发效率,使用一套代码即可实现在Android、iOS和Web平台上运行,节约了成本得到了广大移动开发者的追捧。

本章主要从React Native的发展历程和工作原理等方面对React Native做了一个简单的介绍,并横向比较了当前主流的跨平台方案。纵观目前的跨平台方案,你会发现React Native是目前最好的跨平台技术,如果你的团队正在进行跨平台开发或者考虑跨平台开发,不妨试试React Native。



举报

相关推荐

0 条评论