React Native

React Native

1.React Native之了解

1.1 Native开发优势:

Native的原生控件有更好的体验;

Native有更好的手势识别;

Native有更合适的线程模型,尽管Web Worker可以解决一部分问题,但如图像解码、文本渲染仍无法多线程渲染,这影响了Web的流畅性。

1.2 React Native优势:

1.既拥有Native的用户体验、又保留React的开发效率(RN 通过 JavaScript Core 解析 JavaScript 模块,转换成原生 Native 组件渲染)

2.React Native基本完成了对多端的支持,可以灵活的使用HTML和CSS布局,使用React语法构建组件,实现:H5, Android, iOS 多端代码的复用

3.追求极致的用户体验:实时热部署(CodePush 在修复一些小问题和添加新特性的时候,不需要经过二进制打包,可以直接推送代码进行实时更新。)

4.UI排版的问题:

类似HTML + CSS的排版使用原生控件渲染的框架:

BeeFramework ,BeeFramework虽然开源多年,而且有2000多的star数,但是受限于它自身的影响力以及框架的复杂性,一直没有很大的成功。

React Native采用了类似HTML + CSS的排版,可以内嵌到模块,也可以全局使用,定义样式变得非常简单通用。 引入了Flexbox布局,使用很方便,学习起来也更简单。

5.动态绑定,这个React的基本功能,被带到了客户端开发中来,数据和视图是动态绑定的,数据发生变化,视图会跟着变化,很多操作视图的代码都可以省略了。

6.引入了方便的npm管理,有大量现成的nodejs包可以用(例如moment,underscore等常用模块),还可以把自己项目模块搞到内部npm上做通用组件,另外,npm上还有不少别人写的react native的插件。

7.第三方组件里有一个可以把icon font引入项目的组件,可以在任何显示图标的地方直接用icon font显示

8.调试很方便,一次编译后,每次改了js代码,只需要在模拟器里command+R即可重新加载代码。有问题会直接报错,里面有代码行数等详细信息。

9.完整封装了各种js内置的方法,例如:setTimeout,setInterval,XMLHttpRequest,localstorage,console.log等,都是用oc原生方法封装的。

10.引入ES6的支持,可以使用各种新特性,例如最常用的箭头函数,解决this作用域乱套的问题。

1.3 React Native是什么?

Facebook于2015年9月15日发布React Native

广大开发者可以使用JavaScript和React开发跨平台移动应用.

React Native提倡组件化开发: 即提供一个个封装好的组件,组件相互嵌套形成新的组件

1.4 React Native开发注意事项

目前react native在iOS上仅支持iOS8以上,Android仅支持Android4.1以上版本;

由于React Native的版本更新速度很快,如果没有深厚的JavaScript基础,建议选择:

功能适中,交互一般,不需要特别多的系统原生支持;

对于部分复杂的应用,可以考虑原生+React Native混合开发

学习网站:

github地址:https://github.com/facebook/react-native

官网文档:http://facebook.github.io/react-native/docs/getting-started.html

1.5 React Native开发环境:

参考中文React Native网站:

React Native开发环境配置

2.React Native之学习

2.1 FlexBox布局:

弹性盒模型(The Flexible Box Module),又叫Flexbox,意为“弹性布局”,旨在通过弹性的方式来对齐和分布容器中内容的空间,使其能适应不同屏幕,为盒装模型提供最大的灵活性。

Flex布局主要思想是:让容器有能力让其子项目能够改变其宽度、高度(甚至是顺序),以最佳方式填充可用空间;

Flex学习入门网站:Flex 布局教程

flex基本概念:

采用Flex布局的元素,称为Flex容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称"项目"。

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。

项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

flex常用属性总结:

容器属性:

flex-direction(主轴方向)

flex-wrap(是否换行)

justify-content(item在主轴对齐方式) ,

align-items(item在交叉轴上如何对齐) ,

元素属性:

Flex:弹性宽度:宽度=item该flex值/该容器所有item的flex和*(容器宽度-该容器item没有设置flex的直接宽度)

align-self:性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性

2.2 Touchable系列组件:

高亮触摸  TouchableHighlight:

当手指点击按下的时候,该视图的不透明度会进行降低同时会看到相应的颜色,其实现原理则是在底层新添加了一个View。TouchableHighlight只能进行一层嵌套,不能多层嵌套。

常用属性:

activeOpacity  number  设置组件在进行触摸的时候,显示的不透明度(取值在0-1之间)

onHideUnderlay  function  方法 当底层被隐藏的时候调用

onShowUnderlay  function 方法 当底层显示的时候调用

style   可以设置控件的风格演示,该风格演示可以参考View组件的style

underlayColor  当触摸或者点击控件的时候显示出的颜色

不透明触摸  TouchableOpacity

该组件封装了响应触摸事件;当点击按下的时候,该组件的透明度会降低。等等

代码示例

style={styles.button}

source={require('./button.png')}

/>

style={styles.button}

source={require('image!myButton')}

/>

2.3 组件生命周期:

一:实例化阶段函数分析:

1,getDefaultProps

初始化一些默认的属性,通常会将固定的内容放在这个函数 中进行初始化和赋值;         可以利用this.props获取组件在这里初始化它的属性,组件自己不可以自己修改props(即:props可认为是只读的)

2,getInitialState

用于对组件的一些状态进行初始化;在以后的过程中,会再次调用,所以可以将控制控件的状态的一些变量放在这里初始化,如控件上显示的文字,可以通过this.state来获取值,通过this.setState来修改state值,一旦调用了this.setState方法,组件一定会调用render方法,React框架会自动根据DOM的状态来判断是否需要真正的渲染。

3,componentWillMount

相当于OC中的ViewWillAppear方法.

4,render

render是一个组件中必须有的方法,本质上是一个函数,并返回JSX或其他组件来构成DOM,和Android的XML布局类似,注意:只能返回一个顶级元素 ;可通过this.state和this.props数据 。

5,componentDidMount

在调用了render方法后一般会在这个函数中处理网络请求等加载数据的操作;因为UI已经成功被渲染出来, 所以放在这个函数里进行请求操作,不会出现UI上的错误。

二,存在期阶段函数功能分析:

componentWillReceiveProps

指父元素对组件的props或state进行了修改

shouldComponentUpdate

一般用于优化,可以返回false或true来控制是否进行渲染

componentWillUpdate

组件刷新前调用,类似componentWillMount

componentDidUpdate

更新后的hook

三、销毁期阶段函数功能分析:

用于清理一些无用的内容,如:点击事件Listener,只有一个过程:componentWillUnmount

2.4 请求网络数据:

React Native中通常是通过 Ajax (异步的 JavaScript 和 XML)请求从服务器获取数据,然后在componentDidMount 方法中创建 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI。

2.5 OC, Recat Native混合开发:

直接在iOS项目中写代码就能实现OC,reactNative混合开发,在需要引入React Native 的位置引用该模块即可

AppDelegate.m部分代码

jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation

moduleName:@"TGMeituan"

initialProperties:nil

launchOptions:launchOptions];

index.ios.js 部分代码

AppRegistry.registerComponent('TGMeituan', () => TGMeituan);

xcode的代码引用了index.ios.js文件中的'TGMeituan', index.ios.js其输出了index.ios.js定义的一个TGMeituan组件 AppDelegate.m 中在合适的位置引用rootView,即可实现混合开发

export default class TGMeituan extends Component {

render() {

return (

);

}

}

2.6 ES5和ES6的React Native差异化:

区别1:创建组件

组件是一个自定义的js对象,在es5中使用React.createClass();在es6中必须继承React.component,

区别2:组件的属性props

在ES6中,其为属性:defaultProps(可以标识static定义在class内,也可以定义在class外),而在ES5中,其为方法:getDefaultProps: function(){return {name:value}};

区别3:组件的状态state

上图左为ES5 ,右为ES6

2.7 React Native不足:

组件不全,第三方组件也不全,遇到某些特殊功能,需要捣鼓很久,例如摄像相关的,文件读写,文件上传之类的组件。

性能并非媲美原生,还是有一些损耗的,特别是交换大数据的时候,例如读取相册。

ios和android代码并非通用,有可能会需要维护两套,或者在代码内做一些判断。

并非网上大家说的,写一次代码,多端通用,网页版和客户端版完全不是一个概念,只有部分代码可重用。

把代码都打包到bundle里面,不知道苹果对这种开发方式是否会不太喜欢,甚至拒绝上线。

打包出来的 JSBundle 过大;

首次进入 RN 页面加载缓慢;

稳定性不够,有大量因为 RN 导致的 Crash:

iOS 的 Crash,基本都来自RCTFatalException,都是RCTFatal抛出错误信息所知,处理也相对简单,设置自己的Error Handler即可。 void RCTSetFatalHandler(RCTFatalHandler fatalHandler);

大数据量时 ListView 加载卡顿。

3.ListView重用优化

3.1 ListView不能重用的原因

首先RN的 ListView 其实是基于 RN 的 RCTScrollView 来实现的。它也实现了类似 UIKit 中通过 DataSource 来控制数据,以及是否要做一些界面的刷新

这个View会有一个 RCTView 会引用它。当这个 View 被移出屏幕之外,再观察他的内存引用时,它就只被 RCTUIManager 引用了:

RN 为了能够保持一定的 UI 上的性能,他用 UImanager 来管理所有的 UI 元素,只要创建过的,还有可能被显示在界面上的东西,他都用这个 UImanager 来去管理,从而在进行 Dom Diff 时能够减少 View 的创建和销毁。

3.2 ListView 多做了什么?

然后,我们再来看看 ListView 本身比 RCTScrollView 多做的哪些东西,首先 ListView 包含两个属性 —- initialListSize 和 pageSize ,initialListSize决定了第一屏加载item的数量,pageSize则是当你需要加载更多的时候,每次需要载入多少的item,这样做的主要目的在尽量减少你手机加载第一屏时所需要的时间。

还有就是它还实现了从JS端实现了 Section Header,Header,Footer 的封装,以及实现了监听 onScroll 事件,随着 View 的滚动动态的添加 row view。

3.3 那么ListView相当于UITableView少了一点什么呢?

怎么没有提到复用?

我们先看一下 iOS 的 JS,JS里面只有一行代码

module.exports = require('ScrollView');

3.4 ListView 性能优化解决方案

Bridge 一个 UITableView

在RN中我们要 bridge 一个 RN 的 View 组件,我们需要实现 RCTComponent 这个 protocol,这里有两个很重要的方法

- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex;

- (void)removeReactSubview:(id)subview;

这两个方法是 RN 做 Dom Diff 的关键

什么是Dom Diff呢

在界面发生变化前,界面存在一个 Dom Tree,发生业务变化之后是另外一个 Dom tree,Tree中的每个元素都有自己的引用值,Diff 其实就是找出两个 Tree 的差异点来确定需要进行更新的节点。最终确定一个需要插入和删除的 View 的列表,并通知相应的 Dom 节点来处理。

但是RN的UI处理方式和原生对UI处理完全不一样,我们如何 Bridge 一个 TableView 呢,我们想到了一个方法。

我们创建一些 VirtualView,他只是遵从了 RCTComponent 协议,他其实并不是一个真正的 View,我把它形成一个组件,把它 Bridge 到 JS,这就使得,你在写 JSX 的时候,就可以直接用 VirtualView 来去做布局了。在RN里面做布局的时候我们用VirtualView来做布局。但是最终在 insertReactSubview 时,我们把这些 VirtualView 当做数据去处理,通过 VirtualView 和RealView 的对应关系,把它转化成一个真实的 View 对象添加到 TableView 中去。

用这个图来说,更清晰一些。

首先我们写的是一个 JSX,React 把它转化成 Dom Tree,在进行 Dom Diff 后,React 会调用 insertReactSubview 传入 VirtualView,我们通过 VirtualView 生成 Tree Data, 通过 VirtualView 和 RealView 的对应关系,我们创建 RealView 去真正的添加到原生的 View 上。

但是这里又产生另外一个问题,大家会自定义一个 cell 的一个对象来去做的。这个对象,能够接收你特定的数据,对这个 cell 重新去 set 一些控件的值,然后把界面更新。 但是在JS里面我们并没有办法这样做,在 RN 中,我们不可能动态的去往 Native 里面去加一个类。

那么我们是如何做到,在复用的时候对于 Cell 上面的子View能够去设置更新他的数据?

我们在所有子 view 上面我们也加上了 tag 属性,在更新数据的时候我们通过 tag 找到更新的子 view上面的 view 对他做数据的更新的。所以并不是只有Cell有这样的tag,包括子 view 也会有这样的 tag,这样就做到了可以获取到对应 tag 的子 view 并对子 view 的数据进行更新。

最后,为了客户端的同学在使用这个 TableView 时更好上手一些,我们把几乎整套的 TableViewDataSource 方法,全部照搬到了 RN 中,所以我们在创建这个 ListView 的时候我们需要去设置很多的回调方法,这样做也是为了能够更快的做一些界面的迁移工作。

3.5 ListView 性能优化解决方案的缺点

首先既然它需要做映射,我们肯定需要做一个 Virtualview 到 NativeView,大多数的 cell 里面如果做展示来用的话,Label 和 Image 基本上能够满足大多数的需求了。所以我们现在只是做了 Label 和 Image 的对应工作,但在RN的一些官方控件,在这个 view 里面都是没法直接使用的。

还有一个缺点就是说,因为我们是按照 TableView 的逻辑去做的,这个逻辑其实在 Android 上可能不适用,因为 Android 的 ListView 实现跟iOS完全不是一个逻辑,导致使用这个 ListView 的 RN 代码,可能没法直接应用到 Android 里面去。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容