
前言
Learn once, write anywhere: Build mobile apps with React
Facebook
在React.js Conf 2015
年会议上发布了React Native
(以下简称RN )。RN 是一个使用React
构建Native App
的框架,支持Android 4.1+
和iOS 8+
平台。它充分利用了Facebook
现有的轮子,成为前端和客户端技术的集大成者。
RN兼备H5的动态性和Native
的性能,完美弥补了手机浏览器性能和Native
硬编码的弊端。
RN 的前身是React
,这是一个为了解决项目越来越复杂导致DOM
频繁操作和界面重绘,而诞生的高性能渲染框架。
它只描述了View
层,Facebook 2014
年发布Flux
,以及Redux
等模式框架和React
配套使用。
RN 社区活跃度非常高,有完善的文档和众多贡献者支持,不用担心没资料而苦恼,这是RN 能从同类框架(主要指Weex
)脱颖而出的优势之一。但是也侧面反映了RN 还不稳定,至今依然没有发布1.0版本。目前RN每两周会发布一个小版本,并提供了完善的升级支持。
在正式开始阅读文章之前,希望你具备以下知识:
- JavaScript 基本知识
- 对 Android 或 iOS 平台开发有了解
ES6 语法基础
RN 目前采用
ES6
作为其编程语法标准,ES6全称ECMAScript 6.0
是JavaScript
语言的下一代标准,在2015年6月正式发布。它的目标是使JavaScript
可以用来编写复杂的大型应用程序,成为企业级开发语言。ECMAScript
和JavaScript
的关系是,前者是后者的规范,后者是前者的一种实现。这一章使用最简单的语法带你快速了解ES6,帮助你能理解和阅读RN的代码。如果你很了解ES6请跳过这章节。
想要了解更多,推荐阮一峰的ECMAScript 6 入门
变量的定义
除了常用的var命令和function命令,E6还支持let,const命令
1. let
{
let a = 10;
var b = 1;
}
let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
2. 块作用域
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
上面两个代码块都声明了变量n,运行后输出5。这表示外层代码块不受内层代码块的影响。
{{{{{let insane = 'Hello World'}}}}};
ES6 允许块级作用域的任意嵌套
3. const
const PI = 3.1415;
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
函数的定义
1. 指定函数默认值
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'World') // Hello World
log('Hello', '') // Hello
2. 箭头函数
var f = v => v;
等同于
var f = function(v) {
return v;
};
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
简化回调函数
Class 的基本语法
1. 定义类
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var foo = new Foo();
foo.toString();
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。
const MyClass = class Foo {
getClassName() {
return Me.name;
}
};
与函数一样,类也可以使用表达式的形式定义。
ES6不提供私有方法和属性的支持,常用在方法名前加_
的方式进行区分,如_methodName()
2. 定义实例属性
class Foo {
state = {
count: 0
};
}
3. 定义静态方法和属性
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.prop = 1;
Foo.classMethod() // 'hello'
上面代码使用static
定义了一个静态方法,表示该方法不会被实例继承,而是直接通过类来调用。
Foo.prop = 1;
表示定义了Foo类的一个静态属性。
Module 的语法
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。
1. CommonJS
let { stat, exists, readFile } = require('fs');
上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取3个方法。这种加载称为“运行时加载”。
2. export和import命令
export
命令用于规定模块的对外接口
import
命令用于输入其他模块提供的功能
//将变量声明为外部可用
//方法1
export var firstName = 'Michael';
//方法2
var lastName = 'Jackson';
export {lastName};
//将函数声明为外部可用
export function multiply(x, y) {
return x * y;
};
//将类声明为外部可用
export class { ... }
//加载模块
import {firstName, lastName} from './profile';
import
后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径
注意,import
命令具有提升效果,会提升到整个模块的头部,首先执行。
3. export default 命令
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'
使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载,为了给用户提供方便,要用到export default命令,为模块指定默认输出。这时候customName为任意名字都可进行调用。
组件
组件与生命周期
import React, { Component } from 'react';
import { AppRegistry, Text } from 'react-native';
export default class ReactSample extends Component {
render() {
return (
<Text>Hello ReactNative!</Text>
);
}
}
AppRegistry.registerComponent('ReactSample', () => ReactSample);
上面的代码定义了一个名为ReactSample
的Component
,
- ReactSample
类的render()
方法告诉当前组件要绘制的View;
- AppRegistry.registerComponent()
方法告知RN哪一个组件被注册为整个应用的根容器;
- 常用的组件有<Button> <FlatList> <Image> <Text> <TextInput> <View>
等等。
Props
设置组件的
Props
进行定制
<Image source={{uri: url}} style={{width: 193, height: 110}} />
<Text numberOfLines={1} style={{fontSize: 20}}>Hello ReactNative</Text>
source
和 style
是 Image
组件的Props
numberOfLines
和 style
是 Text
组件的Props
当然实际的属性远不止这些。
State
props是在父组件中指定,一经指定,在被指定的组件的生命周期中则不再改变。 对于需要改变的数据,我们需要使用state。
constructor() {
super();
this.state = {
loaded: false
};
}
上面代码,我们在构造函数中为state
添加了一个属性loaded
表示数据加载的状态。
当数据加载完成后通过以下代码设置改变状态的属性:
this.setState({
loaded: true
});
样式
在 RN 中,你并不需要学习新语法来定义样式。我们仍然像写CSS一样编写我们的样式,这些属性名基本上遵循了CSS的命名,只是改成了驼峰命名法。例如将background-color改为backgroundColor。所有的核心组件都接受名为style的属性。
const styles = StyleSheet.create({
rowTitle: {
marginLeft: 10,
marginRight: 10,
fontSize: 16,
}
});
//使用styles
<Text style={styles.rowTitle}>Hello ReactNative</Text>
手势响应
RN 提供了一组抽象的Touchable实现,用来做“可触控”的组件。这一实现利用了响应系统,使得你可以简单地以声明的方式来配置触控处理。想了解更多手势响应,多指操作实现,请参考Gesture Responder System
<TouchableOpacity onPress={() => Alert.alert("click")}>
<Text >Click..</Text>
</TouchableOpacity>
<TouchableHighlight onPress={() => Alert.alert("click") underlayColor={'#999999'}>
<Text >Click..</Text>
</TouchableHighlight>
<TouchableNativeFeedback onPress={...} background={TouchableNativeFeedback.Ripple('#333333', false)}>
<Text >Click..</Text>
</TouchableNativeFeedback>
TouchableHighlight
,TouchableOpacity
区别仅仅是多了些属性,例如设置underlayColor
,它表示点击时显示出来底层的颜色。
TouchableNativeFeedback
看起来更高级一些,他可以通过background
属性,设置Android主题中的触摸样式,如涟漪效果。
推荐使用以下静态方法快速指定background
:
- TouchableNativeFeedback.SelectableBackground()
: Android
主题默认的按下背景效果。
- TouchableNativeFeedback.SelectableBackgroundBorderless()
:Android
主题默认的按下背景效果(无边框)。
- TouableNativeFeedback.Ripple(color, borderless)
:涟漪效果,可以指定颜色和边界。
<TouchableWithoutFeedback onLongPress ={...}}>
<Text >onLongPress..</Text>
<TouchableWithoutFeedback/>
并不推荐使用TouchableWithoutFeedback
,和其他Touchable
系列相比,没有视觉反馈,多了很多额外的事件
属性,如onLongPress
、onPressOut
等。
生命周期
提到生命周期,无非是创建、更新、销毁。RN 的组件就是一个状态机,它接收两个输入参数:
props
和state
,返回一个Virtual DOM
。换句话说组件的状态变化取决于props
和state
,和原生一样,RN也为我们提供相应的回调函数。
componentWillMount
: 准备渲染组件前(第一次调用reader()
之前)调用。render
: 渲染界面,务必实现的方法。componentDidMount
:组件第一次渲染之后调用。componentWillUnmount
: 当组件要被从界面上移除的时候调用。componentWillReceiveProps
:当组件收到新的属性(props),就会触发该方法。shouldComponentUpdate
:当组件收到新的属性和状态改变时,就会触发该方法。componentWillUpdate
:shouldComponentUpdate
返回true
之后,准备更新组件界面之前。componentDidUpdate
: 组件更新完界面(调用reader()
)之后。
简单看一下组件的生命周期,记住reader
是必须实现的你就可以继续往下阅读了。
Flexbox布局
RN中的Flexbox工作原理和Web上CSS Flexbx布局方式基本一致,当然也存在少许差异。比如默认值不同:flexDirection的默认值是column而不是row。
元素
采用Flex布局的元素,称为flex container,简称"容器"。它的所有子元素自动成为容器成员,称为flex item,简称"项目"。

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start
,结束位置叫做main end
;交叉轴的开始位置叫做cross start
,结束位置叫做cross end
。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size
,占据的交叉轴空间叫做cross size
上面这张图是Flex布局的精髓,看懂了就会用了,下面的属性介绍就可以当参考资料了~
容器的属性
flexDirection
:容器主轴排列方向
box: {
flexDirection: row | row-reverse | column | column-reverse
}
row
: 从左端开始,水平(横)方向。row-reverse
: 从右端开始,水平(横)方向。column
: 默认值,从上方开始,垂(纵)直方向。column-reverse
: 从下方开始,垂(纵)直方向。
flexWrap
:默认情况下Item都排到一条线上,该属性定义换行方式。
box: {
flexWrapp: nowrap | wrap | wrap-reverse
}
nowrap
: 不换行。wrap
: 换行,第一行在上面。wrap-reverse
: 换行,第一行在下面。
flexFlow
:flexDirection
和flexWrap
的复合属性
box: {
flexFlow: <flex-direction> || <flex-wrap>
}
justifyContent
:定义了Item在主轴上的对齐方式
box: {
justifyContent: flex-start | flex-end | center | space-between | space-around;
}
flex-start
:左对齐。flex-end
:右对齐。center
: 居中。space-between
:两端对齐,Item间隔相等。space-around
:Item与边框两侧间隔相等,Item之间间隔比Item与边框间隔大一倍。
alignItems
:交叉轴的对齐方式
box: {
alignItems: flex-start | flex-end | center | baseline | stretch;
}
flex-start
:起点对齐。flex-end
:终点对齐。center
:居中对齐。baseline
: Item第一行文字基线对齐。stretch
:如果Item未设置高度或设为auto,将占满整个容器的高度。
alignContent
:定义了多根轴线的对齐方式。如果Item只有一根轴线,该属性无效
box: {
alignContentt: flex-start | flex-end | center | space-between | space-around | stretch;
}
flex-start
:起点对齐。flex-end
:终点对齐。center
:居中对齐。space-between
:两端对齐,轴线之间的间隔平均分布。space-around
:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。stretch
:轴线占满整个交叉轴。
Item属性
order
:Item的排列顺序,数值小的在前。flexGrow
:Item的放大比例。如果存在剩余空间,根据数值进行放大。flexShrink
:Item的缩小比例。如果空间不足,根据数值缩小。flexBasis
:在分配多余空间之前,项目占据的主轴空间固定大小。flex
:flexGrow
、flexShrink
、flexBasis
的复合属性。alignSelf
:允许单个Item有与其他Item不一样的对齐方式。
案例
rowCell(rowData) {
return (
<View style={styles.container}>
<Image style={styles.rowHead} source={{uri: 'https://xxx.jpg'}}/>
<Text style={styles.rowTitle}>Hello ReactNative~</Text>
</View>
);
}
const styles = StyleSheet.create({
rowHead: {
height: 40,
width: 40,
borderRadius: 50
},
rowTitle: {
marginLeft: 10,
marginRight: 10,
fontSize: 16,
},
container: {
margin: 10,
alignItems: 'center',
justifyContent: 'flex-start',
flexDirection: 'row'
}
}
上面的代码很简单,定义了一个其Item水平排列,起点对齐其的container,包含<Text>
、<Image>
两个元素。