React Native是Facebook推出用JavaScript开发iOS和Android应用框架,它跟Cordova和Titanium等用JavaScript创建原生应用的框架有本质的不同,React Native通过编译出来的包就是个原生应用而不是嵌入Web的hybrid应用。所有它的性能比其它开发原生应用的JavaScript框架高出很多。

Build native mobile apps using JavaScript and React

React Native实现一套机制用于JS层于原生模块层通信。(详细介绍文章

React Native 优缺点
优点:

  • 使用React思想涉足移动端开发,提高原生开发效率。
  • 能够利用JavaScript特性进行免安装快速更新。
  • 相对于其它Hybird框架性能更好。

缺点:

  • React Native无法做到只用JavaScript开发原生应用!开发必须要有原生应用开发的经验
  • 性能无法达到原生应用。
  • 学习门槛高,学习成本大。Web开发者需要了解原生开发细节,原生开发者需要理解React开发思想。

本文只介绍React Native开发涉及到前端部分!因为博主完全不懂原生应用开发 :)

基础

React Native沿袭了 React组件化开发思想,UI开发方式和数据管理方式和React一样。

数据状态

React Native中有两种方式用于控制数据:propsstate
props是在父组件中指定,而且一经指定就无法更改。而state是在组件初始化构造函数constructor中声明的。在组件的生命周期和方法内可以通过setState方法改变它的值。

创建一个组件显示父组件传递的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';

class Greeting extends Component {
render () {
return (
<Text>{this.props.name}!</Text>
)
}
};

export default class LotsOfGreetings extends Component {
render () {
return (
<View style={{alignItems: 'center'}} >
<Greeting name='Rexxar' />
<Greeting name='Jaina' />
<Greeting name='Valeera' />
</View>
)
}
};

AppRegistry.registerComponent('ProjectName', ()=>LotsOfGreetings);

创建一个组件,每个1秒钟就闪屏一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';

class Blink extends Component {
constructor (props) {
super(props);
this.state = {isShowingText: true};

setInterval( () => {
this.setState(previousState => {
return {isShowingText: !previousState.isShowingText};
})
}, 1000)
}

render () {
let display = this.state.isShowingText ? this.props.text : ' ';
return (
<Text>{display}</Text>
)
}
};

export default class BlinkApp extends Component {
render () {
return (
<View>
<Blink text='I love to bink' />
<Blink text='Yes blinking is so great' />
<Blink text='Why did they ever take this out of HTML' />
<Blink text='Look at me look at me look at me' />
</View>
)
}
};

AppRegistry.registerComponent('ProjectName', () => BlinkApp);

在构建复杂的应用时,使用setState更新数据并不是明智的选择。对于复杂的数据业务,应该使用数据管理器控制数据的流向。Redux是目前流行的数据管理框架。

样式

React Native中可以通过JavaScript来写样式,语法类似于CSS只是属性名使用的是驼峰命名法。例如:background-color改为backgroundColor

通过StyleSheet.create集中定义组件的样式(类似将样式写在<style></style>)。将StyleSheet.create中定义的样式赋予组件上的style属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';

export default class LotsOfStyles extends Component {
render() {
return (
<View>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigblue}>just bigblue</Text>
<Text style={[styles.bigblue, styles.red]}>bigblue, then red</Text>
<Text style={[styles.red, styles.bigblue]}>red, then bigblue</Text>
</View>
);
}
}

const styles = StyleSheet.create({
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});

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

如上所示,如果style有多个值应该将它们用如一个数组中。

React Native中使用flexbox布局页面,它的语法和Web的CSS基本一致,它们不同点是,flexDirection的默认值是column而不是row。而flex只能指定一个数字。
React Native中组件设计的尺寸方式是指定固定的widthheight。而它们都是没有单位的,它们于设备像素密度无关的逻辑像素点,在不用尺寸的屏幕上都显示成一样的大小。

网络请求

React Native提供了和Web标准一致的Fetch API用于网络请求数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fetch('https://mywebsite.com/endpoint',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
})
}).then( response => response.json())
.then( responseJson => {
return responseJson.movies;
}).catch ( error=>{
console.error(error);
});

提交数据的格式关键取决于 headers 中的 Content-Type。不同的 Content-Type 对应的body的格式也有区别。是使用传统的 application/x-www-form-urlencoded 还是目前流行的 application/json需要前后端沟通好。

React Native也支持WebSocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var ws = new WebSocket('ws://host.com/path');

ws.onopen = () => {
// 打开一个连接

ws.send('something'); // 发送一个消息
};

ws.onmessage = (e) => {
// 接收到了一个消息
console.log(e.data);
};

ws.onerror = (e) => {
// 发生了一个错误
console.log(e.message);
};

ws.onclose = (e) => {
// 连接被关闭了
console.log(e.code, e.reason);
};

通用组件

Text Input

TextInput 键盘输入文本的基本组件。通过给onChangeText属性赋予方法,每当输入框文本变化时会触发该绑定的方法。而通过给onSubmitEditing属性赋予方法,当用户点击提交按钮是会触发该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { Component } from 'react';
import { AppRegistry, Text, TextInput, View } from 'react-native';

export default class PizzaTranslator extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}

render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
/>
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
</View>
);
}
}

// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => PizzaTranslator);

TextInput组件属性支持还自动获取焦点(autoFocus)、自动大小写(autoCapitalize)、多种不同的键盘(keyboardType)

ScrollView

ScrollView是个通用的滑动容器,其中可以放置多个组件和视图,但是它适合列表数量不多且固定的滑动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { Component } from 'react';
import { AppRegistry, ScrollView, Image, Text } from 'react-native';

export default class IScrolledDownAndWhatHappenedNextShockedMe extends Component {
render() {
return (
<ScrollView>
<Text style={{fontSize:96}}>Scroll me plz</Text>
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Text style={{fontSize:96}}>If you like</Text>
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Text style={{fontSize:96}}>Scrolling down</Text>
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
<Image source={require('/react-native/img/favicon.png')} />
</ScrollView>
);
}
}

// skip these lines if using Create React Native App
AppRegistry.registerComponent(
'AwesomeProject',
() => IScrolledDownAndWhatHappenedNextShockedMe);

ScrollViews通过配置pagingEnabled属性支持滑动切换页面。

FlatList

FlatList高性能的列表组件,支持下面这些常用的功能:

  • 完全跨平台
  • 支持水平布局模式
  • 行组件显示或隐藏时可配置回掉事件
  • 支持单独的头部组件
  • 支持单独的尾部组件
  • 支持自定义行间分割线
  • 支持上拉加载
  • 支持跳转到指定行

FlatList组件有两必定义属性datarenderItemdata是列表的源数据。renderItem从源数据取一项用于定义好格式的组件渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View } from 'react-native';

export default class FlatListBasics extends Component {
render() {
return (
<View style={styles.container}>
<FlatList
data={[
{key: 'Devin'},
{key: 'Jackson'},
{key: 'James'},
{key: 'Joel'},
{key: 'John'},
{key: 'Jillian'},
{key: 'Jimmy'},
{key: 'Julie'},
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
})

// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => FlatListBasics);

SectionList

高性能的分组(section)列表组件,支持下面这些常用的功能:

  • 完全跨平台
  • 支持水平布局模式
  • 行组件显示或隐藏时可配置回调事件
  • 支持单独头部组件
  • 支持单独尾部组件
  • 支持自定义行间分隔线
  • 支持下拉刷新
  • 支持上拉刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React, { Component } from 'react';
import { AppRegistry, SectionList, StyleSheet, Text, View } from 'react-native';

export default class SectionListBasics extends Component {
render() {
return (
<View style={styles.container}>
<SectionList
sections={[
{title: 'D', data: ['Devin']},
{title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},
]}
renderItem={({item}) => <Text style={styles.item}>{item}</Text>}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22
},
sectionHeader: {
paddingTop: 2,
paddingLeft: 10,
paddingRight: 10,
paddingBottom: 2,
fontSize: 14,
fontWeight: 'bold',
backgroundColor: 'rgba(247,247,247,1.0)',
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
})

// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => SectionListBasics);

SectionList用于需要分组展示的数据。

View

View是React Native中最基础的组件。它通常用于界面的布局(flexbox,style),和绑定touch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ViewColoredBoxesWithText extends Component {
render() {
return (
<View
style={{
flexDirection: 'row',
height: 100,
padding: 20,
}}>
<View style={{backgroundColor: 'blue', flex: 0.3}} />
<View style={{backgroundColor: 'red', flex: 0.5}} />
<Text>Hello World!</Text>
</View>
);
}
}

StyleSheet

StyleSheet是对CSS的StyleSheets 的抽象。它用于创建样式规则赋予一个变量分配给节点上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const styles = StyleSheet.create({
container: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: '#d6d7da',
},
title: {
fontSize: 19,
fontWeight: 'bold',
},
activeTitle: {
color: 'red',
},
});

<View style={styles.container}>
<Text style={[styles.title, this.props.isActive && styles.activeTitle]} />
</View>

Button

Button按钮的基础组件能够很好的在各个平台上展示,支持有限的自定义。

1
2
3
4
5
6
7
8
9
import { Button } from 'react-native';
...

<Button
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
  • onPress 当用户触摸时触发方法。
  • title 按钮中展示的文字。
  • accessibilityLabel 无障碍显示的文字。
  • color 设置iOS文本颜色,设置Android背景颜色。
  • disabled 如果为true,则组件无法点击。
  • testID 用于端对端测试

Picker

Picker选择器。

1
2
3
4
5
6
<Picker
selectedValue={this.state.language}
onValueChange={(itemValue, itemIndex) => this.setState({language: itemValue})}>
<Picker.Item label="Java" value="java" />
<Picker.Item label="JavaScript" value="js" />
</Picker>
  • onValueChange 当数据变化时触发的方法。这个方法有两个参数
    • itemValue 选中的item的值。
    • itemPosition 被选中item的序号
  • selectedValue 默认选中的item。
  • mode 选择器的模式。有两种:dialogdropdown 默认是 dialog 。(Android)

WebView

WebView渲染web页面

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { Component } from 'react';
import { WebView } from 'react-native';

class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
  • source 加载静态的 html 或 uri在WebView中。
  • style 设置组件样式。

iOS组件和API

AlertIOS

AlertIOS创建iOS消息提示框。

1
2
3
4
5
6
7
8
9
10
AlertIOS.alert(
'Sync Complete',
'All your data are belong to us.'
);

AlertIOS.prompt(
'Enter a value',
null,
text => console.log("You entered "+text)
);

DatePickerIOS

DatePickerIOSiOS平台上选中日期和时间组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { Component } from 'react'
import {
DatePickerIOS,
View,
StyleSheet,
} from 'react-native'

export default class App extends Component {
constructor(props) {
super(props);
this.state = { chosenDate: new Date() };

this.setDate = this.setDate.bind(this);
}

setDate(newDate) {
this.setState({chosenDate: newDate})
}

render() {
return (
<View style={styles.container}>
<DatePickerIOS
date={this.state.chosenDate}
onDateChange={this.setDate}
/>
</View>
)
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
})

这个组件上你必须定义onDateChang回调方法和date初始化数据。

Android组件

DatePickerAndroid

DatePickerAndroid使用标准的Android日期选择器。

1
2
3
4
5
6
7
8
9
10
11
12
try {
const {action, year, month, day} = await DatePickerAndroid.open({
// Use `new Date()` for current date.
// May 25 2020. Month 0 is January.
date: new Date(2020, 4, 25)
});
if (action !== DatePickerAndroid.dismissedAction) {
// Selected year, month (0-11), day
}
} catch ({code, message}) {
console.warn('Cannot open date picker', message);
}

TimePickerAndroid

TimePickerAndroid使用标准的Android时间选中器。

1
2
3
4
5
6
7
8
9
10
11
12
try {
const {action, hour, minute} = await TimePickerAndroid.open({
hour: 14,
minute: 0,
is24Hour: false, // Will display '2 PM'
});
if (action !== TimePickerAndroid.dismissedAction) {
// Selected hour (0-23), minute (0-59)
}
} catch ({code, message}) {
console.warn('Cannot open time picker', message);
}

ViewPagerAndroid

ViewPagerAndroid容器运行左右翻动子视图。每个子视图是独立且会沾满ViewPagerAndroid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
render: function() {
return (
<ViewPagerAndroid
style={styles.viewPager}
initialPage={0}>
<View style={styles.pageStyle} key="1">
<Text>First page</Text>
</View>
<View style={styles.pageStyle} key="2">
<Text>Second page</Text>
</View>
</ViewPagerAndroid>
);
}

...

var styles = {
...
viewPager: {
flex: 1
},
pageStyle: {
alignItems: 'center',
padding: 20,
}
}

参考

https://facebook.github.io/react-native/docs/getting-started.html