React Native笔记01

常用组件

文本输入 TextInput
导航 Navigator

View
Text
ScrollView
Image
ListView
TouchableHighlight
TouchableNativeFeedback
TouchableOpacity
TouchableWithoutFeedback
ViewPagerAndroid

常用引入包

Redux来统一管理数据流

网络库 frisbee、axios
防止定时器clear时出bug react-timer-mixin (ES5)
持久数据 Immutable
reselect

安装

python2
npm 包管理
node 环境
react-native-cli 脚手架
Nuclide FaceBook的IDE

1
2
3
4
5
6
7
8
9
10
11
12
13
npm 替换淘宝镜像源
1.通过config命令
npm config set registry https://registry.npm.taobao.org
npm info underscore (如果上面配置正确这个命令会有字符串response)
2.命令行指定
npm --registry https://registry.npm.taobao.org info underscore
3.编辑 ~/.npmrc 加入下面内容
registry = https://registry.npm.taobao.org

自定义组件

Component

1
2
3
4
5
6
7
8
9
10
11
12
13
函数 render()、construtor(props)
class Greeting extends Component {
render() {
return (
<Text>Hello {this.props.name}!</Text>
);
}
}
===========
<Greeting name='文字'/>

弹性Flex 宽高

flex可以使其在可利用的空间中动态地扩张或收缩
flex:1来指定某个组件扩张以撑满所有剩余的空间

使用Flexbox布局

flexDirection 指定主体方向、容器属性

1
flexDirection: 'row' 水平 、默认竖直 column

justifyContent 指定子元素排列方式、容器属性

1
flex-start、center、flex-end、space-around以及space-between

alignItems 子元素沿着次轴方向、容器属性

1
flex-start、center、flex-end以及stretch

处理文本输入

基础组件 TextInput

属性 onChangeText 接受一个函数

1
onChangeText={(text) => this.setState({text})}

属性 onSubmitEditing 会在文本被提交后(用户按下软键盘上的提交键)调用。

ScrollView

默认垂直。 水平horizontal

ListView

ListView组件必须的两个属性是dataSource和renderRow.
dataSource是列表的数据源
renderRow则逐个解析数据源中的数据

rowHasChanged函数也是ListView的必需属性、判断某行数据是否变化了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
])
};
}
============
<View style={{flex: 1, paddingTop: 22}}>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>}
/>
</View>

网络

react-native 内置了XMLHttpRequest API

支持 WebSocket

使用Fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
请求、 Fetch 方法会返回一个Promise
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
})
})

处理服务器的响应数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getMoviesFromApiAsync() {
return fetch('http://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
}
=============ES7
// 注意这个方法前面有async关键字
async getMoviesFromApi() {
try {
// 注意这里的await语句,其所在的函数必须有async关键字声明
let response = await fetch('http://facebook.github.io/react-native/movies.json');
let responseJson = await response.json();
return responseJson.movies;
} catch(error) {
console.error(error);
}
}

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);
};
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
import React, { Component } from 'react';
import { AppRegistry, Navigator, Text, View } from 'react-native';
import MyScene from './MyScene';
class SimpleNavigationApp extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) =>
<MyScene
title={route.title}
// Function to call when a new scene should be displayed
onForward={ () => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene ' + nextIndex,
index: nextIndex,
});
}}
// Function to call to go back to the previous scene
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
}
/>
)
}
}
AppRegistry.registerComponent('SimpleNavigationApp', () => SimpleNavigationApp);
AppRegistry.registerComponent('SimpleNavigationApp', () => SimpleNavigationApp);

跳转

1
2
3
4
5
6
navigator.push({
title: 'Next Scene',
index: 1,
});
navigator.pop();

颜色格式支持

1
2
3
4
5
6
7
8
9
10
11
'#f0f' (#rgb)
'#f0fc' (#rgba)
'#ff00ff' (#rrggbb)
'#ff00ff00' (#rrggbbaa)
'rgb(255, 255, 255)'
'rgba(255, 255, 255, 1.0)'
'hsl(360, 100%, 100%)'
'hsla(360, 100%, 100%, 1.0)'
'transparent'
'red'
0xff00ff00 (0xrrggbbaa)

图片

静态图片资源

1
2
3
4
5
6
<Image source={require('./my-icon.png')} />
Packager就会去这个组件所在的文件夹下查找my-icon.png。并且,如果你有my-icon.ios.png和my-icon.android.png,Packager就会根据平台而选择不同的文件。
你还可以使用@2x,@3x这样的文件名后缀,来为不同的屏幕精度提供图片。

使用混合App的图片资源

1
<Image source={{uri: 'app_icon'}} style={{width: 40, height: 40}} />

网络图片

1
<Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}} style={{width: 400, height: 400}} />

本地文件系统中的图片

1
2
3
4
详情见,Images.xcassets
require('image!logo')

资源属性是一个对象Object

1
2
3
4
5
<Image source={{uri: 'something.jpg'}} />
裁剪
{uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}}ß

实现背景

1
2
3
4
5
return (
<Image source={...}>
<Text>Inside</Text>
</Image>
);

圆角

1
2
3
4
5
6
7
iOS暂不支持
borderTopLeftRadius
borderTopRightRadius
borderBottomLeftRadius
borderBottomRightRadius

处理触摸事件

可点击控件,Touchable开头

  • TouchableHighlight来制作按钮或者链接
  • Android上还可以使用TouchableNativeFeedback,类似墨水涟漪的视觉效果
  • TouchableOpacity会在用户手指按下时降低按钮的透明度,而不会改变背景的颜色。
  • TouchableWithoutFeedback, 不显示任何视觉反馈。
1
2
3
4
5
6
7
8
9
10
11
12
13
class MyButton extends Component {
_onPressButton() {
console.log("You tapped the button!");
}
render() {
return (
<TouchableHighlight onPress={this._onPressButton}>
<Text>Button</Text>
</TouchableHighlight>
);
}
}

长按

1
onLongPress属性

在列表中上下滑动和在视图上左右滑动

1
ScrollView可以在垂直或水平方向滚动,还可以配置pagingEnabled属性来让用户整屏整屏的滑动。此外,水平方向的滑动还可以使用Android上的ViewPagerAndroid 组件。

双指缩放

1
在ScrollView中只放置一个组件,设置maximumZoomScale和minimumZoomScale

动画

Animated

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
class Playground extends React.Component {
constructor(props: any) {
super(props);
this.state = {
bounceValue: new Animated.Value(0),
};
}
render(): ReactElement {
return (
<Animated.Image // 可选的基本组件类型: Image, Text, View
source={{uri: 'http://i.imgur.com/XMKOH81.jpg'}}
style={{
flex: 1,
transform: [ // `transform`是一个有序数组(动画按顺序执行)
{scale: this.state.bounceValue}, // 将`bounceValue`赋值给 `scale`
]
}}
/>
);
}
componentDidMount() {
this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值
Animated.spring( // 可选的基本动画类型: spring, decay, timing
this.state.bounceValue, // 将`bounceValue`值动画化
{
toValue: 0.8, // 将其值以动画的形式改到一个较小值
friction: 1, // Bouncier spring
}
).start(); // 开始执行动画
}
}
spring: 基础的单次弹跳物理模型,符合Origami设计标准
friction: 摩擦力,默认为7.
tension: 张力,默认40。
decay: 以一个初始速度开始并且逐渐减慢停止。
velocity: 起始速度,必填参数。
deceleration: 速度衰减比例,默认为0.997。
timing: 从时间范围映射到渐变的值。
duration: 动画持续的时间(单位是毫秒),默认为500。
easing:一个用于定义曲线的渐变函数。阅读Easing模块可以找到许多预定义的函数。iOS默认为Easing.inOut(Easing.ease)。
delay: 在一段时间之后开始动画(单位是毫秒),默认为0。

组合动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
parallel(同时执行)、sequence(顺序执行)、stagger和delay来组合使用。
Animated.sequence([ // 首先执行decay动画,结束后同时执行spring和twirl动画
Animated.decay(position, { // 滑行一段距离后停止
velocity: {x: gestureState.vx, y: gestureState.vy}, // 根据用户的手势设置速度
deceleration: 0.997,
}),
Animated.parallel([ // 在decay之后并行执行:
Animated.spring(position, {
toValue: {x: 0, y: 0} // 返回到起始点开始
}),
Animated.timing(twirl, { // 同时开始旋转
toValue: 360,
}),
]),
]).start(); // 执行这一整套动画序列

插值 interpolate

1
2
3
4
5
6
7
8
9
10
11
value.interpolate({
inputRange: [0, 1],
outputRange: [0, 100],
});
支持多区间定义
value.interpolate({
inputRange: [-300, -100, 0, 100, 101],
outputRange: [300, 0, 1, 0, 0],
});

跟踪动态值

1
2
3
4
5
6
7
Animated.spring(follower, {toValue: leader}).start();
Animated.timing(opacity, {
toValue: pan.x.interpolate({
inputRange: [0, 300],
outputRange: [1, 0],
}),
}).start();

LayoutAnimation 允许你在全局范围内创建和更新动画

1
2
3
如果要在Android上使用LayoutAnimation,那么目前还需要在UIManager中启用:
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);

setNativeProps 方法可以使我们直接修改基于原生视图的组件的属性,而不需要使用setState来重新渲染整个组件树。

定时器

setTimeout, clearTimeout
setInterval, clearInterval
setImmediate, clearImmediate
requestAnimationFrame, cancelAnimationFrame

TimerMixin(ES5)

1
2
3
4
5
6
7
8
9
10
11
12
13
这个库并没有跟着React Native一起发布。你需要在项目文件夹下输入npm i react-timer-mixin --save来单独安装它。
var TimerMixin = require('react-timer-mixin');
var Component = React.createClass({
mixins: [TimerMixin],
componentDidMount: function() {
this.setTimeout(
() => { console.log('这样我就不会导致内存泄露!'); },
500
);
}
});

ES6代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React,{
Component
} from 'react';
export default class Hello extends Component {
componentDidMount() {
this.timer = setTimeout(
() => { console.log('把一个定时器的引用挂在this上'); },
500
);
}
componentWillUnmount() {
// 如果存在this.timer,则使用clearTimeout清空。
// 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear
this.timer && clearTimeout(this.timer);
}
};