React Native
React Native大家好,今天跟大家分享的主题是《从iOS到React Native》,接下来的内容主要分为四个方面:弄清几个概念:React、React Native及其原理写简单Demo: 跳转、UI、数据、生命周期推荐的入门路径目前的一些问题和困扰React 、React Native概念React现在app有很多新兴的概念,我们先把React Native的相关概念问题搞清楚
React Native
大家好,今天跟大家分享的主题是《从iOS到React Native》,接下来的内容主要分为四个方面:
- 弄清几个概念:React、React Native及其原理
- 写简单Demo: 跳转、UI、数据、生命周期
- 推荐的入门路径
- 目前的一些问题和困扰
React 、React Native概念
React
现在app有很多新兴的概念,我们先把React Native的相关概念问题搞清楚。首先介绍一下React。
在几年前,Facebook的工程师在开发中对当时市面上所有的前端框架都不满意,就决定自己写一套,用来架设 Instagram 的网站,起名“React”。做出来以后,发现这套东西很好用,就在2013年5月开源了。
由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。目前很多公司的网站前端也是全面铺开React框架。
React有两个非常出色的地方:
- 虚拟DOM
- 数据的单向流动
虚拟DOM
我们先看下虚拟DOM的示意图:
我们知道,web中的UI和数据都是放在DOM树中的。按照正常的DOM思维,如果我们先要刷新DOM树的b节点,那么会向DOM树发出刷新b节点的命令,但是在刷新的时候,也会把b所影响的其他的节点一起刷新,造成刷新实际过程非常复杂,时间也会较长。我咨询了一下web的开发,其实在开发中是有办法去单独刷新b节点的,但是因为涉及到的逻辑比较多,会占用一些开发时间,所以很多的人就选择简单的让DOM树刷新b节点。
React提供了虚拟DOM的思维来解决这个问题,本质上是“以空间换时间”。它会在系统中生成预期刷新后的DOM树,然后和当前的DOM树做一个diff操作,然后只刷新需要改变的DOM节点,极大地提高了DOM树的刷新速率。
数据单向流动
React第二个非常出色的地方就是数据的单向流动。当然,这个并不是React的标配和发明,是开发者在使用过程中,发现数据单向流动有利于设计,所以使用了数据单向流动的思路。
我们在写界面的一些UI或者交互逻辑的时候,经常会出现多个控件互相去修改UI属性或者修改数据的情况,如果页面的逻辑比较多,或者页面的层级复杂的时候,就会发现修改状态等的逻辑到处都是,对于后面的拓展或者是修复bug等都造成了困扰。
而使用数据单向流动这套逻辑, 你不用关系具体的View如何变化,而是修改View绑定的数据,当你使用React提供的方法去修改View的时候,它会自动调用渲染(render)方法,然后通过刚才讲的虚拟DOM的原理去更新UI。
这套方案,极大地降低了逻辑的复杂性,让大家能够将更多的精力花在数据上面。而且将渲染(render)的方法收了口,每次的渲染都在同一个入口,也方便调试。
React Native
我们的发版计划,受到很多的制约和限制,主要是AppleStore的审核,经常会出现各种各样的问题导致版本审核不通过,会打乱运营的一些推广计划或者促销计划。而且审核也需要几天的时间,导致很难及时地更新app内容。另外如果出现了紧急的bug,虽然我们有热修复,但是效果毕竟还是差了些。大家都希望能随时随地更新app中的内容,减少发版时间的束缚。
为了实现这个目标,出现了各种各样的解决思路。前段时间有一个比较火的概念:”hybrid“,其主要思想就是app提供一个”壳“,里面放webView,所有的页面都是h5的。这样就做到了,随时更新内容,并且开发一套代码,可以iOS、android、h5通用,降低了开发的成本,提高了效率。
但是Hybrid本质上还是web的形式,所以性能和交互还是比app差了一截,效果差强人意。Facebook提出了另一种思路,就是使用原生代码封装一套UI组件,以js为语法,在React的基础上,开发了React Native的框架。它的口号是:“learn once,write anywhere”,就是说学习一次,可以在iOS和android上开发。注意,并不是写一套,两个端通用,因为iOS和android的机制和特点还是有很多的区别,所以有时候要因地制宜,根据不同的情况去处理。但是实践发现,大部分的还是可以通用的,有一些要根据效果来调整。

React Native机制
React Native分别使用iOS和android的原生控件,提供了一套UI组件,给开发者用js来调用,从而实现了一套代码,两端都可以适配,而且性能、交互体验基本和native一致。
在React Native中,我们使用js语法,js会通过虚拟DOM的方式去渲染,然后到了iOS和android会被转义成不同的native控件。
React Native 实现
看完了ReactNative的机制,我们来看下在iOS中, ReactNative究竟是如何实现的。在这里,我们和Hybrid来做比对,加深大家的理解。

Hybrid是在WebViewController上面加载不同的H5,而RN的话,就是在我们自己封装的RNViewController上面加载不同的js文件,不同的是,我们的RNViewController上面使用了RN提供的RCTRootView,将js的命名传入即可。
React Native通信
看完了RN的实现,我们来看下RN是如何和Native通信的。先看下图:

js和native的交互主要是通过js Bridge 和 OC Bridge来实现的,调用方式是通过回调。
Demo
看完RN(React Native)的基本概念和原理,我们看下如何书写一个简单的Demo。
- 要把本地环境配好,安装好RN、npm等库。
- 其次是把本地npm服务跑起来,至于跑起来是为了什么我们等下讲。
- 然后iOS的话要修改配置,主要是白名单和RN loader url的配置。
我们看下为什么要做第二步和第三步,这里要讲下RN的js的载入原理。
在Release中,我们会把js代码都打包成一个js bundle包,然后放到app的Document中,但是在debug中的时候,我们希望实时从代码中打包载入,这时候就要把js bundle包的地址只想npm server的地址,然后npm server就会动态地去打包js。
这里要讲下RN的一个优点,就是它的语言是js,js是一个脚本语言,所以是不需要提前编译的,我们在debug的时候就不需要像app debug那样繁琐,每次都重新编译代码,然后重启模拟器,只需要使用快捷键,重新载入js包即可。
新建文件
需要的环境和配置都改好后,我们新建一个js文件,这里用的IDE是webStorm。
- 我们新建一个file,命名为test.js。
- 打开index.js 在里面注册test.js
这里讲下RN的一个特点,就是如果我有个文件,在iOS和Android中内容不同,没办法统一的话,可以分成两个文件,比如index.js,可以分别写两个文件,index.ios.js,index.android.js,RN在载入文件的时候,会根据当前的platform,载入不同的文件。
好了,文件建好了,如何跳转到这个页面呢?RN和Native的交互,全部采用OpenUrl,我们可以把RN当成一个普通的ViewController,至于具体的js就是它的一个参数。
OpenUrl格式:
pageName=TNReactNativeViewController¶meters={“rctModuleName”:string(对应index.js注册的component),"rctModuleParams": json, 对应传⼊入component的props
这里面有几个参数:
pageName 这个是页面的名称,对RN来说就是固定的,iOS和Android不同
rctModuleName:js文件的名称,我们这里就是”test”
rctModuleParams: 参数,是一个json键值对
iOS的跳转代码如下:
//参数 Dictionary
NSDictionary *params = @{@"rctModuleName":@"HelloWorld",};
//Dictionary to string
NSString *paramsString = [params tn_JSONString];
//string encode
NSString *paramsStringEncode = [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//拼openUrl
NSString *openUrl = [NSString stringWithFormat:@"tuniuapp://page?"
@"pageName=TNReactNativeViewController&"
@"parameters=%@",paramsStringEncode];
//跳转
[TNNavigationService to:openUrl];
HelloWorld
下面我们开始RN的helloWorld
RN的js文件有三个基本的部分:
- import 你引用的文件和类
- class(render) 定义你的class,render是一定要实现的方法,输出你的UI
- export 对外输出的内容
代码如下:
import React from 'react';
import {
Text,
} from 'react-native';
class HelloWorld extends React.Component {
render(){
return(
<Text>HelloWorld<Text>
)
}
}
export default HelloWorld;
数据
下面讲下RN的数据问题,数据类型主要分成两类:
- props 属性 不可变 是外部给的入参
- state 状态 可变 内部的可变状态
我的理解是,props是父View给的入参,而state是我内部去记录一些状态用,当props和state改变的时候,都可能改变我当前的UI,这部分内容在生命周期的时候会讲。
我们来看个实例,刚才在openUrl的时候,我们讲了如何给RN传参数是在rctModuleParams中,我们传个参数试试
刚才的openUrl的代码就改成:
//参数 Dictionary
NSDictionary *params = @{@”rctModuleName”:@”HelloWorld”,@”rctModuleParams”:@{@”name”:@”this is props”}};
//Dictionary to string
NSString *paramsString = [params tn_JSONString];
//string encode
NSString *paramsStringEncode = [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//拼openUrl
NSString *openUrl = [NSString stringWithFormat:@"tuniuapp://page?"
@"pageName=TNReactNativeViewController&"
@"parameters=%@",paramsStringEncode];
//跳转
[TNNavigationService to:openUrl];
这次我们将参数中,增加rctModuleParams,在里面写入name,这样外面的传值操作就完成了。
接下来我们要在RN里面接收这个参数,先看代码
import React from 'react';
import {
Text,
} from 'react-native';
class HelloWorld extends React.Component {
render(){
return(){
<Text>this.props.name<Text>
}
}
}
export default HelloWorld;
好了代码已经改好了,我们来看下效果,这里顺便讲下rn是如何调试的。因为js是脚本语言,不需要预先编译,所以就不要把代码重新编译一遍,然后再跑模拟器,只需要使用快捷键(xcode 是 cmd + R),然后我们刚才讲的npm server,就会把当前的js代码重新打成一个js bundle包,然后页面刷新就看到最新的效果了。
生命周期
数据的内容讲完了,我们来看下这个页面的生命周期。说实话,我觉得写RN框架的人之前一定是做iOS出身,为什么这么说呢?因为他使用的很多命名和函数名,都是神似iOS的风格。包括我们接下来要说的页面的生命周期,其实和iOS的页面生命周期也非常的像。我们先看下图:
首先页面会初始化当前的props和state、然后会进入页面即将绘制(componentWillMount)的状态,我们的初始化工作,一般都是在这个状态。
接下来会绘制界面,然后进入页面已经绘制(componentDidMount)的状态。接下来在运行中,这里用了我们最开始讲的React的一个特性,就是数据的单向流动,修改了Props或者State都会让页面进入即将更新的状态,然后会走到(shouldComponentUpdate),这个函数默认是返回yes的,就是一旦数据变化,就重新绘制,但是我们可以重写这个函数,来做一些性能的优化。
接下来就进入到componentWillUpdate,然后就重新绘制页面了,接下来是componentDidUpdate。
在页面即将被卸载的时候,还会进入componentWillUnmount,这里要做的是把一些接收通知等时间注销掉,然后整个页面的生命周期就结束了。
入门路径
好,我们简单地讲了RN的一个小Demo,接下来看下推荐的入门路径。
我在一个月前刚刚接触RN的时候,也是很痛苦,因为完全不了解这个RN他是怎么玩的,很多的概念也不是很明白,另外就是它使用的语言是js,而我之前又一直做iOS开发的,所以很是手足无措的感觉。现在做了一个多月的迭代,总算是半入门了,如果推荐的话,我觉得可以按照下面的路径入门:
1. 配置环境
2. debug 尝试修改当前的代码和组件
3. 学习UI:flex布局
4. 数据:props、state、reduex
5. 语法:es6
6. 官网看api
配置环境当然是必须的,这里大家可以参考我们的官网说明
我建议大家先跑一跑代码,点一点,看一看功能,大概感受下RN到底能做什么。然后可以尝试修改一些代码,比如text或者颜色等属性,再深一点可以写一个简单的组件放到某个页面上,这样的话,写出来的代码是和你有互动的,你也能接收到反馈,能提供给你对RN的整个概念的理解。
接下来可以学习简单的UI、各种控件都写一写,尝试下api,这里重点要推荐Flex布局,这个是RN UI的基本思路,一定要看下。
然后可以学习数据,props和state是基本内容。然后看下当前我们项目用的数据框架reduex,其实reduex不是RN提供的数据框架,是一个第三方框架,RN提供了一个叫flux的数据框架,但是因为没那么好用,所以我们现在都是用reduex来做。
接着就说到了语法的问题,其实我在写的过程中遇到的语法问题比较少,因为大部分的代码都是用的基本语法,和OC都是想通的,只有少数几个比较特殊的语法需要大家看下,不过有时间的话还是建议看下es6的语法,对写出来更漂亮的代码还是有帮助的。
最后就是要去官网看下api,这里要提醒的是有个react native中文网,那个王章入门的时候可以看下,但是它并不是真正的官网,而是国内的几个人翻译过来的,所以有些内容的质量没那么好,另外就是可能更新的速度不如官网。
目前的一些问题和困扰
接触了RN一个多月,和之前写iOS代码,感觉还是有一些不习惯的东西,或者说是现在遇到的问题。
1. 报错不准确,这个是我觉得最严重的问题,就是RN的提示很多时候不正确,可能五个不能问题,但是最终报的错误都是同一个,不能给我很准确的提示去找问题所以,只能一行行地去看刚才修改的东西,比较痛苦。
2. iOS和android的区别,这个是因为我写代码是用的iOS的模拟器来看效果的,但是写完了之后看android,很多效果比如动画,在iOS中比较合适的,在android中就没法让人接受,还是有很多的区别。
3. 复杂的效果不好做。之前我们的设计师设计了一个毛玻璃效果,这个在iOS中有组件去做这个事情,非常简单,但是RN我们目前就没有找到方案,设计师就问我这个应该是很简单的效果,怎么就做不出来。这个也要大家在做RN的时候和产品、设计等提前去沟通。因为现在的RN还处在比较初始的阶段,提供的组件不多,第三方好用的组件也比较少。
4. 第三方比较牛的组件少,这个和第四个问题可以一起说,就是现在从事RN的人还不是那么多,生态圈还不是很强大,所以好用的第三方组件少,很多时候需要我们自己来造轮子,效果也不是那么好。
更多推荐


所有评论(0)