序言
这篇教程旨在让你使用 React Native 快速开发 iOS 和 Android 应用。如果你想知道什么是 React Native 以及为什么 Facebook 构建了它,可以阅读相关文章来了解背景。我们假设你已有使用 React 开发应用的经验;如果没有,建议先学习 React 基础知识。
安装
React Native 需要一些基本的安装依赖,具体步骤可参考官方文档。安装完成后,通过以下两条命令来初始化一个新项目:
- 运行
npm install -g react-native-cli来全局安装 React Native 命令行工具。 - 运行
react-native init AwesomeProject来创建一个新项目,这会生成 iOS 和 Android 工程文件。
开发
在 iOS 端,你可以在 Xcode 中打开生成的项目文件,然后使用 ⌘+R 构建和运行,同时开启 Node 服务器支持代码实时渲染。在 Android 端,运行 react-native run-android 来安装应用到模拟器,并开启 Node 服务器;通过设备震动菜单(模拟器中按 F2 或 Page Up)选择 "Reload JS" 来查看更改。
本教程将引导你开发一个简单的电影应用,用于获取并显示 25 部电影信息。
Hello World
react-native init 生成的默认应用是一个简单的 "Hello World" 程序。你可以编辑 index.ios.js(iOS)或 index.android.js(Android)来修改应用,然后通过 ⌘+R 或 "Reload JS" 查看变化。
伪造数据
在获取真实数据之前,我们可以先用模拟数据开始。在 index.ios.js 或 index.android.js 文件顶部定义常量:
var MOCKED_MOVIES_DATA = [
{title: 'Title', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}},
];
渲染一部电影
我们需要渲染电影的标题、年份和缩略图。首先,在 React 引入中增加 Image 组件:
var {
AppRegistry,
Image,
StyleSheet,
Text,
View,
} = React;
然后修改渲染函数来显示模拟数据:
render: function() {
var movie = MOCKED_MOVIES_DATA[0];
return (
<View style={styles.container}>
<Text>{movie.title}</Text>
<Text>{movie.year}</Text>
<Image source={{uri: movie.posters.thumbnail}} />
</View>
);
}
此时运行应用,你会发现图片未显示,因为缺少宽度和高度样式。接下来定义样式并应用到图片:
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
thumbnail: {
width: 53,
height: 81,
},
});
将样式添加到 Image 组件:<Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} />,重新加载后图片即可正常渲染。
增加一些样式
现在让布局更美观:将文字放在图片右侧,并调整标题和年份的样式。修改渲染函数如下:
return (
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
更新样式对象,使用 FlexBox 进行水平布局:
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8,
textAlign: 'center',
},
year: {
textAlign: 'center',
},
重新加载应用,你会看到更新后的视图,文字和图片布局更加合理。
获取真实数据
现在从远程 API 获取真实数据。在文件顶部定义请求 URL:
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
为组件添加初始状态,用于跟踪数据加载情况:
getInitialState: function() {
return {
movies: null,
};
},
在组件加载完成后发起数据请求:
componentDidMount: function() {
this.fetchData();
},
实现 fetchData 方法处理数据获取:
fetchData: function() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
movies: responseData.movies,
});
})
.done();
},
修改渲染函数,在数据未加载时显示加载视图,否则渲染第一部电影:
render: function() {
if (!this.state.movies) {
return this.renderLoadingView();
}
var movie = this.state.movies[0];
return this.renderMovie(movie);
},
renderLoadingView: function() {
return (
<View style={styles.container}>
<Text>
Loading movies...
</Text>
</View>
);
},
renderMovie: function(movie) {
return (
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
},
重新加载应用,你会先看到 "Loading movies...",然后渲染出获取到的第一部电影数据。
ListView
为了渲染所有电影数据,我们改用 ListView 组件。首先在引入中增加 ListView:
var {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
} = React;
修改渲染函数,使用 ListView 来显示数据列表:
render: function() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
);
},
更新初始状态,包括一个空的 dataSource 和加载状态:
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
},
调整 fetchData 方法,将获取的数据设置到 dataSource:
fetchData: function() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
loaded: true,
});
})
.done();
},
为 ListView 添加样式:
listView: {
paddingTop: 20,
backgroundColor: '#F5FCFF',
},
现在重新运行应用,你将看到一个完整的电影列表。这只是一个基础示例,实际应用中可以添加导航栏、搜索框等功能来完善。
最终源代码
以下是完整的代码示例,供参考:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
} = React;
var API_KEY = '7waqfqbprs7pajbz28mqf6vz';
var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json';
var PAGE_SIZE = 25;
var PARAMS = '?apikey=' + API_KEY + '&page_limit=' + PAGE_SIZE;
var REQUEST_URL = API_URL + PARAMS;
var AwesomeProject = React.createClass({
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
},
componentDidMount: function() {
this.fetchData();
},
fetchData: function() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
loaded: true,
});
})
.done();
},
render: function() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
);
},
renderLoadingView: function() {
return (
<View style={styles.container}>
<Text>
Loading movies...
</Text>
</View>
);
},
renderMovie: function(movie) {
return (
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
},
});
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8,
textAlign: 'center',
},
year: {
textAlign: 'center',
},
thumbnail: {
width: 53,
height: 81,
},
listView: {
paddingTop: 20,
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);