概述:
React Native 是现在比较火的Android APP开发技术,由FaceBook推出的基于JSX开发的一个可以跨平台开发的框架。 Facebook在这个框架中提出了一个理念: Learn once, write anywhere 也是这个框架的优势所在。目前基于RN开发的APP有:
天猫iPad客户端,支付宝,携程网,Facebook Group,Chinese Flashcards等等(部分业务、模块采用RN开发)。
下图是RN实践的demo:
Step 1: 环境的搭建(mac)
- 安装Homebrew;
- 安装nvm;
- 安装node,通过命令:
nvm install node && nvm alias default node
; - 安装watchmam,通过命令:
brew install watchman
; - 安装flow,通过命令:
brew install flow
; - 安装React Native,通过命令:
npm install -g react-native-cli
; - 创建React Native应用程序,通过命令
react-native init HelloWorld
; - 进入HelloWorld目录,并创建5.0系统以上的安卓模拟器;
- 配置React Native环境,通过命令
npm install
; - 运行React Native程序,通过命令
react-native run-android
。
Step 2: React Native项目的基本认识
进入HelloWorld目录后,我们至少看到以下几个文件或者文件夹:
文件夹
android
ios
node_modules文件
index.androi.js
index.ios.js
package.json
android、ios文件夹分别对应ios工程和android工程,两个js文件分别对应android、ios默认的js文件,package.json和package.json是React Native的环境配置相关文件。
Android工程依赖于指定的js展示界面,全工程甚至可以只需要通过一个Activity和多个js文件来表现不同的页面。在js文件中,Facebook提供了UI组件,网络请求等其他APIS,类似于于Activity+XML的结合体。
在这里不得不提的是,Facebook在最早的时候提出基础理念叫做React,基于此出现了ReactJS来开发网页,直到现在出现React Native来开发移动应用。在React Native官网中提到这么一句话:
React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React.
也就是说React Native是基于JavaScript和React的开发框架,因此支持JS语法。
Step 3: JS文件语法的初步学习
打开index.androoid.js,观察里面的代码。从上往下解读:
代码的开始以:
import ... from ...
的方式引入该界面所需要的组件元素。
中间创建了一个HelloWorld组件,在最早的RN版本中,使用React.CreateClass
的形式创建组件,在最新的RN版本中使用了:
extends Component
的方式代替React.CreateClass
,因此在很多教程中出现不一样的代码。
接下来是
const styles = StyleSheet.create({})
这里才用CSS Style来对UI进行管理。
最后
AppRegistry.registerComponent('HelloWorld', () => HelloWorld);
注册组件,单引号内的HelloWorld对应MainActivity中的getMainComponentName()
方法返回的指定的组件名称,后面的HelloWorld是我们在上面通过extends Component
的方式创建的component。
重点观察class HelloWorld
中的内容:
render() {
return {};
};
在render方法中返回了类似于“div”标签形式组合的ui组件,通过修改对应的文本,并双击R(Genymotion)ReLoad JS,可以查看修改后的内容。
通过访问React Native官网,在左侧的导航列表的COMPONENTS
列表中可以看到通用的UI控件,如ListView
,Image
等等。参考官网提供的学习步骤,我们可以新建一个js文件,建立自己的Movie List程序,效果图如上图所示。
Step 4: JS文件的编写
新建movielist.js,并保存在目录HelloWorld工程的根目录下(与index.android.js目录平级),修改MainActivity中的getJSMainModuleName()
方法返回的String字符串"index.android"
为"movielist"
,引入相应控件,Image
,Text
等:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
} from 'react-native';
创建HelloWorld组件,完成render()
方法,返回movie list程序的第一个item:
class HelloWorld extends Component{
render() {
return {
<View style={styles.item}>
<ImageView style={style.img} source={{require('./img/icon.png'})} />
<Text style"styles.text">movie item</Text>
</View>
}
}
}
const styles = StyleSheet.create({
item: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
padding: 10,
},
img: {
width: 100,
height: 100,
justifyContent: 'center',
},
text: {
width: 200,
fontSize: 10,
textAlign: 'left',
}
});
其中style属性同CSS的应用方法大同小异,可以查阅官网左侧导航栏底部的POLYFILLS
导航进行查阅。
Image组件设置图片的方式两种,
本地:`source={require('./img/icon.png')}`;
网络:`source={{uri: item.url}}`
如果想获取到屏幕的宽度或者高度来控制控件的宽高,可通过Dimensions
:
import {
Dimensions
} from 'react-native';
const styles = StyleSheet.create({
text: {
width: Dimensions.get('window').width - 150,
fontSize: 10,
textAlign: 'left',
}
});
item编写完成后,导入ListView组件,完成ListView的编写:
ListView是需要数据源和item view的,因此修改刚才编写好的代码,将render()
方法中返回的UI组件抽取为rendItem()
方法,并重写render()
方法
import { ListView } from 'react-native';
class HelloWorld extends Component{
render() {
return {
<ListView
dataSouce={}
renderRow={this.renderItem}
></ListView>
}
}
renderItem(){
return {
<View style={styles.item}>
<ImageView style={style.img} source={{require('./img/icon.png'})} />
<Text style"styles.text">movie item</Text>
</View>
}
}
}
继续为ListView补充数据,采用从网络获取的方式,Facebook提供了fetch()函数进行异步网络请求:
var REQUEST_URL = "https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json"
class HelloWorld extends Component{
constructor() {
super();
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2 })
};
}
componentDidMount() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
});
})
.done()
}
}
在这里,constructor()
和componentDidMount()
和render()
方法一样,为RN组件的生命周期,在通过 constructor()
声明数据,componentDidMount()
方法进行数据设置,调用this.setState()方式强制重新调起render()
方法刷新视图。在React.createClass
的语法中,constructor()
方法等同getInitialState()
方法,都是用来初始化状态值,可用来改变组件状态。
在componentDidMount()
方法中通过fetch(REQUEST_URL)
得到相应值response
并进行转化为相应的数据格式,取对应的值赋值dataSource
。
注意这里的dataSource
必须通过:
dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2 })
的方式进行初始化,rowHasChanged
是ListView的dataSource的必须属性,用来进行渲染的优化,类似于ViewHolder。
补全代码,并修改item的数据:
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderItem}>
</ListView>
)
renderItem(movie){
return {
<View style={styles.item}>
<ImageView style={style.img} source={{uri: movie.posters.thumbnail}} />
<Text style"styles.text">{movie.title}</Text>
</View>
}
}
}
至此,movie list程序已经基本完成,接下来添加点击item的点击事件。这里我们采用组件:TouchableHighlight。修改renderItem(movie)
方法:
renderItem(movie){
return {
<TouchableHighlight onPress={this.alertImage}>
<View style={styles.item}>
<ImageView style={style.img} source={{uri: movie.posters.thumbnail}} />
<Text style"styles.text">{movie.title}</Text>
</View>
</TouchableHighlight>
}
}
alertImage(){
alert("点击了一条item");
}
Facebook一共提供了四个Android的点击组件,基本点击组件为TouchableWithoutFeedback
,其余的点击组件可以视为由TouchableWithoutFeedback
发展过来的,TouchableWithoutFeedback
是无点击出发效果的控件,而诸如TouchableHighlight
则是可以通过style配置点击的动画背景效果的,可以通过TouchableWithoutFeedback等查询相应的属性。
同时,点击事件提供了按下时、松开后、长按、短按四个动作事件,同样在API上查询到,并且要注意的是:onPress
的执行者是一个Object,而不是调用this.alertImage()
,这是不被允许的语法。
上面我们写了最简单的点击事件,但是并没有出现对应的效果,经过查询原因,发现this.alertImage()
的作用域并不正确。这里的this
并不是指代的事我们认为的组件this,而是在执行renderRow={this.renderItem()}
的时候改变了this的指向,因此我们可以通过bind()
方法来修正这一个错误:
render() {
return {
<ListView
dataSouce={}
renderRow={this.renderItem.bind(this)}
></ListView>
}
}
重新运行,发现点击之后会弹出一个弹出框。但是如果想传递点击条目对应的数据,这个时候我们发现因为只能通过Object指定点击事件,所以我们不能像调用方法那样补写参数。这个时候,我们同样可以用bind()
方法实现目的:
renderItem(movie){
return {
<TouchableHighlight onPress={this.alertImage.bind(this,movie)}>
<View style={styles.item}>
<ImageView style={style.img} source={{uri: movie.posters.thumbnail}} />
<Text style"styles.text">{movie.title}</Text>
</View>
</TouchableHighlight>
}
}
alerImage(movie) {
alert(movie.id);
}
Step 5: 总结
通过简单的初步的学习,我们了解了大致的RN应用程序交互是如何实现的。简单来说是Facebook提供了一套js解析库,开发者通过创建RN工程编写JS文件由JS库解析得到相应的事件动作。因此学习的方向包括组件的应用及封装,API和RN生命周期的学习。
在这里推荐可以学习研究的Demo:
Demo: Reading;
干货*;
OSChina的Git@OSC客户端