您的位置:首页 > Web前端 > React

初探React Native

2016-07-13 19:34 357 查看
PS该报告偏向理论层面,技术层面由于本人才疏学浅属于前端小白因此暂时未作过多研究。



一、React Native相关介绍

简介

React Native是Facebook开发的一套框架,其目的在于使用JavaScript语言开发原生APP,目前iOS版本和Android版本均已开源。

框架定位

下图摘自React Native官网首页:



翻译:

React Native使你能够基于javaScript和React开发世界级的原生APP,其焦点在于开发效率,因为你能够用它来开发多个平台的应用(包括iOS和Android)。换句话说:learn once, write anywhere. Facebook目前已经使用React Native开发了多个原生应用,并将持续完善这套框架。

可以看到,React Native框架本身的定位在于提升移动端应用开发效率。再看看一个“webView”打天下的Hybrid应用,React Native是否也能够一套代码实现两个平台的“一劳永逸”呢?虽然我觉得不能说完全否定,但是基本是不可行的。

官方对于React Native的描述是 learn once, write anywhere ,而并非是 write once,run anywhere. 也就是说,React Native的跨平台,指的是可以用React Native编写两个平台的应用,而不是一套React Native代码可以同时在两个平台上跑。原因在于React Native开发的是原生应用,iOS端和Android端的大部分控件是无法整合到同一个JS端组件或API上的。事实上,只有少数的组件及API能够垮平台使用。因此,简单地说,一个应用,依旧两套代码,只不过可以由一套框架(并不绝对)、一个RN团队来完成。



(以下对“React Native”或以“RN”简称)

涉及技术栈

JSX语法

说到RN的语法,不得不提到React(即React.js)。React同样是脸书开源的一套前端框架,核心语言javaScript毋庸置疑,另外其采用组件化的方式构建界面,这在语法上则需要HTML的支持;对于界面布局,采用的则是一贯的CSS方案。可能你要一脸懵逼了,心想这不就是大名鼎鼎的前端三剑客吗?是的!然而React将HTML、CSS、javaScript黑魔法般地整合到一起,自成规则,成就了一种全新的语法:JSX。而React Native采用的,正是React的这套语法。说白了,React+RN,JS一统三端了!另外,RN已经支持了ES6语法。

服务器端

使用Node.js作为javaScript的运行环境,其内置了V8引擎。就作为一名RN开发者而言,对于Node的认识即便一片空白实际上也无伤大雅。

二、React Native运行(基于iOS)

环境搭建(MAC OS)

安装软件包管理工具 homebrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"[/code] 
安装node.js(或者从官方下载 node.js

brew install node


安装watchman,watchman用来监视文件并且记录文件的改动情况

brew install watchman


安装flow(flow来为js代码加上类型检查,根据需要是否安装)

brew install flow


更新上述程序,保持最新版本(本人配置好环境后未执行该命令更新上述程序版本,工程运行报错,更新后运行正常)

brew update && brew upgrade


安装RN的命令行工具,通过npm安装

npm install -g react-native-cli


Ok,万事俱备!

创建工程

通过命令行创建第一个RN工程learn_rn:
react-native init learn_rn
运行完毕后会在当前目录创建一个 learn_rn 文件夹,这个就是RN工程所在的文件夹。

open learn_rn
工程目录如下:



iOS与android文件夹 :默认创建的iOS工程和android工程文件夹

index.ios 与 ndex.android :分别为iOS和Android工程对应的JS入口文件

package.json :配置文件

node_modules :react native依赖库

回忆刚刚执行
react-native init learn_rn
是不是install了很长时间。原因有二。

react native依赖库体积庞大,查看 node_modules 文件夹大小,足足90多兆;

react native依赖库是从npm服务器拉取的,而npm服务器并不在国内,可以通过翻墙来解决此问题,也可以通过修改npm镜像源。目前npm最常用的是淘宝的镜像源。

运行工程

现在,打开iOS文件夹,运行.xcodeproj工程文件,再熟悉不过的XCode界面。直接Run,最终模拟器会显示一个默认的界面。



分析一下从Run到界面加载的过程发生了什么,首先是JS端的启动工作:

编译原生工程代码

编译完毕之后,自动启动(在XCode做了配置)Node.js服务器

从JS入口文件开始,分析模块之间的依赖关系,转换JSX语法,把转换后的模块打包成一个bundle.js文件并部署到Node服务器。至此,JS端初始化完毕。

接着是原生端的启动工作:

如同正常的iOS工程一样,调用main函数,创建UIApplication实例,创建AppDelegate实例,启动消息循环,开始监听app的运行。

app启动后,方法didFinishLaunchingWithOptions被调用,这个方法中根据jsCodeLocation创建了一个RCTRootView对象,这个jsCodeLocation就是之前打包好的bandle.js地址,是从server上访问的。

原生与JS开始通信,最终在原生端创建好相应的控件显示在界面上。

热加载

来对项目作一点小小的修改。打开index.ios.js文件,暂且不对源码作介绍。将源码中 “Welcome to React Native!” 修改成 “Hi!I’m Lotheve”,保存。 这个时候,打开模拟器,然后
Command+R
,神奇的事情发生了,界面瞬间刷新成修改后的效果。

前端程序猿估计要直呼大惊小怪了,然而对于一个没见过什么世面的原生程序猿,习惯了对任何源码的修改都要重新Run一次工程以适应修改,这样的体验简直要飞!揪其原因,很简单,js代码是跑在服务器上的,每次reload,服务器重新加载代码,实时刷新原生界面(事实上,并不是刷新整个UI,而是只刷新diff部分,这在下面将会介绍)

原生嵌入React Native

在刚才的介绍中有提到,RN的界面是通过一个RCTRootView对象显示的,这个RCTRootView是UIView的子类,因此很容易地可以在原生中嵌入React Native。具体步骤如下:

安装React Native依赖库

通过cocoaPods安装需要的React Native依赖工程

创建index.ios.js入口文件并编写相应的代码

原生引入头文件#import <RCTRootView.h>,在响应的代码区域创建RCTRootView对象并使用

运行Node服务器

运行工程

三、React Native特性

UI更新逻辑

原生中,界面的更新往往由控制器来控制,RN则是通过“状态机”的机制来驱动UI更新的。RN中,整个UI是一个Component树,由一个个Component构成,最终被编译成一个虚拟DOM,加载到内存中。另外,每一个Component可以定义一种叫“state类型”的属性,当这种类型的属性值发生变化时,系统会定位到响应的DOM组件,并计算出DOM的diff信息,根据这个“diff”来驱动UI的更新。

RN的这种UI更新机制,保证了每次只刷新需要目标UI,避免的整个UI的刷新,从而保证了UI渲染的效率。React Native UI更新机制如下图:



React Native通信机制

对于一个RN应用程序,毫无疑问,主调方必然是native端。native解析JS脚本,JS脚本在执行过程中调native,因此考虑的主要是JS怎么去调native。

就iOS而言,React Native与原生之间的通信并不是采用JavaScriptCore或者WebKit提供的可以让两者互调的一些接口,而是在此基础上自己实现了一套通信机制(不同于JSPatch则是完全基于JavaScriptCore来通信的),这套机制能够通用于所有JS引擎上,从而与安卓的V8引擎形成了一种通用的机制。

关于React Native通信机制,bang的这篇博客个人觉得已经分析得非常到位,我就说一下我对React Native通信机制的简单理解。

RN中JS调native主要是借助一个叫“模块配置表”的东西,它本质是一个json文件。原生暴露给JS的类及方法,称之为模块类和模块方法。在模块配置表中,保存了这些模块类的集合以及类中模块方法对应的的集合。这个模块配置表会分别保存一份在native端和JS端,当JS调用原生的某个方法时,从JS端的模块配置表中找到对应的模块类id和模块方法id以及参数,然后传递给native端,从native端的模块配置表中还原出对应的原生方法调用之。

简单的流程如上所述,如果要回调,会在JS端保存回调的代码,然后创建对应的callBackID,将配置信息添加到JS端的模块配置表中。传递模块ID、方法ID、参数ID的同时将这个callBackID一同传递过去。nstive端接收到传过来的callBackID后,会创建一个native端的回调方法(对于iOS而言会创建一个block)。当要执行毁掉的时候,native端的回调方法被调用,方法实现中将callBackID回传给JS端(iOS端借助JS引擎调JS方法很容易)。JS端拿到这个callBackID,从自己的模块配置表中根据callBackID寻找之前保存的对应的方法体,调用之。

以上就是JS与native整体的交互过程,但是有一个问题在上面的过程中没有说明,那就是JS调用native时是怎么把模块ID、方法ID、callBackID、参数传递给native端的?答案是借助于事件机制。JS端并不是主动将这次数据传递给native端,而是由native主动来取的。下图中可以看到JSBridge中有个Message Queue,这是一个队列,里面保存了要调用的原生方法对应的模块ID等数据。native端借助于计时器,按照一定的时间间隔调用JS的指定方法,每次调用,JS会从这个Message Queue中取队头的数据,以返回值的形式回传给native端。当然,JS也是能主动传递数据给native端的,但是这样做的好处是,将调用的主动权给予了native,避免了过多的调用挤入native使得线程来不及处理。至于间隔多久会来取一次调用数据,这必然是一个深度测试权衡的结果。另外,如果native发生意外不来取数据,JS端也不会无限期等下去,也会有一个等待时间的阈值,过时就会主动将调用数据传过去。

下面附上大神博客中给出的调用流程图:



四、React Native性价比分析

优缺点分析

优点:

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

原生级别的交互体验

相对于原生平台,开发速度更快;相对于Hybrid平台,性能更好。

缺点:

做不到 Write once, Run everywhere,也就是说开发者依然需要为 iOS 和 Android 平台提供两套不同的代码,比如参考官方文档可以发现不少组件和API都区分了Android和iOS版本。即使是共用组件,也会有平台独享的函数。

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

由于RN版本目前还处于萌芽期,版本更新那叫一个快。然而有的迭代更新,会将部分支持老版本的代码在新版本中废除,使得原先开发的APP在版本迭代之后运行直接崩溃。这个问题目前无解,你根本就无法预测下一版本会做什么修改,而只能去适应修改!除非你永远不需要更新你的应用!

官方对版本迭代的说明是每两个星期更新一次,目前官方official版本为0.29(当前时间2016.7.13)



由于安卓版本刚开源没多久,资料匮乏。

学习成本

RN是面向前端开发人员提供的移动端开发框架,个人认为,对于不懂前端技术的移动开发者来说,学习RN还是有很大学习成本的。

尽管npm提供了大量的RN组件或者API,但是万一现有代码包无法满足实际需求,就要自己定制原生模块,通过桥接的方式在RN中使用。因此RN开发不能做到完全屏蔽iOS端或Android的技能,开发者必须对原生平台有所了解。这样看来,要想吃透RN,几乎是通吃3个端了,学习成本是非常高的。

五、总结

RN的诞生,不过是给web开发者向移动进军提供了桥梁,对于移动开发者,我认为并未有太大吸引力,至少对于我而言如此。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS RN