React Native 教程



前言


这个教程就是帮您加速如何使用 React native 开始iOS和 Android 的 apps 开发。如果您还不了解 React Native是什么,也不知道Facebook 为啥要研发这个,那这篇博客可以为您解释。

我们也假设您已经有用React 写过应用程序的经验。如果不是这样,您可以在React 官方网站上了解到如何开始。

基本设置:


React native 需要一些基本的设置,这个我们在 React Native起步文章里面都已经做了介绍。

在安装完那些程序的依赖环境之后,这有两条命令帮助您把 React Native 项目的开发环境也都设置好。

npm install –g react-native-cli

react-native-cli 是一个命令行接口,该程序会执行余下来的所有设置工作。 该命令行工具是通过npm 工具安装的。这条命令就会所 react-native 做为一条命令安装在您的终端命令行中。您只需执行一次这条命令就足够了。

React-native init AwesomeProject

这条命令会从服务器上拿回 React Native 的资源代码和一些附属插件,然后会在AwesomeProject/iOS/AwesomeProject.xcodeproj 里创建一个新的XCode 项目,并且在AwesomeProject/android/app创建一个 gradle项目。

开发


对于 iOS,  您现在就可以在 XCode (AwesomeProject/ios/AwesomeProject.xcodeproj)里直接打开这个新的项目了,直接通过 ⌘+R就可以开始编译运行这个程序了。在执行这个命令的同时,系统也会启动一个 Node 服务器,该服务器也支持实时代码加载。所以有了这个功能您就可以随时通过点按 ⌘+R在您的模拟器中查看代码的改变,而不需要在 XCode里面重新编译。

对于Android 用户,在 AwesomeProject 中运行 react-native run-android,把生成的 app文件安装在您的模拟器或是设备之上,同时也要启动Node服务器以保证可以进行实时代码加载。要看您的修改是否生效,您可以打开 rage-shake-menu(要不就是晃动设置或者就是按设置上的菜单按钮,或者按 F2,在模拟器上面是按 Page Up, 在 Genymotion里面是按⌘+M),然后再按下重新加载JS.

我们要做的这个例子是一个电影 app,会从数据库里面拿下来 25 部正在电影院上映的电影然后用列表组件的形式展示出来。

Hello World.


React-native init 可以生成一个以你项目命名的 app,在我们这个例子中叫做 AwesomeProject.  这就是一个简单的Hello World app. 在 iOS中,您可以修改 index.ios.js 文件来改变 app,然后再点按 ⌘+R 在模拟器里面去看看这些变化。对于 Android, 您可以编辑 index.android.js 文件进行修改,然后在 rage shake菜单里点按 Reload JS 去看变化。

模拟数据


在我们真正的开发代码去从烂柿子网站拿实际的数据之前,让我们先模拟一些数据让我们对 React Native有一个简单的了解先。 对于 Facebook的习惯,一般都会在 JS 的文件顶部做变量定义, 就象下面所列出的文件一样,但其实您可以根据您的习惯在任何地方定义变量。 把下面的代码加入到您的 index.ios.js 或者是index.android.js中去:

var MOCKED_MOVIES_DATA = [
  {title: 'Title', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}},
];


展示一部电影

我们打算把电影的片名,上映时间以及电影缩略图展示出来。 因为缩略图是 React Native 中的一个图片组件,所以加入图片到下面 React 所需要的列表中去。

var {
  AppRegistry,
  Image,
  StyleSheet,
  Text,
  View,
} = React;


现在可以修改 render 函数,就可以把上面所提到的那些参数都加入到前面那个您好世界的文件里去了:

 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> ); }

按 ⌘+R / Reload JS后就会看到页面上显示出 Title 下面跟着 2015. 注意,我们的图片组件并没有加载。这是因为我们还没有指定装载图片的容器的大小了。 这个是通过 styles组件来完成了。现在我们可以来试着改写我们需要的 style了,对于那些不要的样式我们也可以直接去掉。
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
});

最后我们需要把这个样式表赋值给图象组件:


<Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} />

再次点按⌘+R / Reload JS 后会发现图片组件被成功加载了。


加入一些样式


太棒了,现在就让我们来美化一下我们的数据吧。让页面显示的更好看点。 我打算将文本文件放在图片的右边,然后让标题显示的更大一点,并且居中显示:
+---------------------------------+
|+-------++----------------------+|
||       ||        Title         ||
|| Image ||                      ||
||       ||        Year          ||
|+-------++----------------------+|
+---------------------------------+

我们需要添加另外一个容器以便于能够在这个水平排列的组件中加入一个垂直排列的组件。


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> );


并没有改变太多,我们只是有文本文件周围加了一个容器并且把他们移到了图片的后面(因为我们计划把文字排在图片的右边)。 让我们看看样式表最后是啥样的:
 container: { flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', },

我们的页面布局使用的是 FlexBox, 可以在这个很棒的指导文件中了解更多相关的信息。

在上面的代码片段里面,我们也只添加了一行: flexDirection: ‘row’ 该条命令就能让我们子容器里的内容采用是水平排列而不是默认的垂直排列。

现在也把其他样式表的信息加入 JS style 对象中:

 rightContainer: { flex: 1, },

 rightContainer 标签里的命令信息表明该对象要占据的位置是在父容器中没有被图像所占据的位置空间了。 假如这个地方您没有搞清楚,那就加一个 backgroundColor 到该样式表中,然后试着把 flex: 1 去掉。 现在您就会注意到当前的容器刚刚好是能容纳下子容器的大小了。

样式化文字的命令就非常直白了:
title: { fontSize: 20, marginBottom: 8, textAlign: 'center', }, year: { textAlign: 'center', },

继续点按 ⌘+R / Reload JS,看看我们刚刚做的样式是否都已经生效了。

获取真实的数据


从那个烂西红柿的 API 上面取回数据其实和我们学习 React Native并没有直接的关系,所以如果在这一章节中您感觉有点跟不上的思路了也不用担心。

把下面所定义的常量都加入到文件的顶部(一般放于 requires 之下),创建一个 REQUEST_URL 用来去提取数据。
/**
 * For quota reasons we replaced the Rotten Tomatoes' API with a sample data of
 * their very own API that lives in React Native's Github repo.
 */
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

添加一些初始化的状态到程序里面,以便于方便我们去检查这样的参数: this.state.movies === null,用来查看是否相关的电影数据已经被成功加载。 我们可以通过响应 this.setState({movies: moviesData})来设置这个数据。把这些代码直接加到 React 类之上就可以了:
getInitialState: function() { return { movies: null, }; },

我们是希望在组件加载结束后立刻就发出索要数据的请求。  componentDidMount 是 React 组件的一个函数,在执行的过程中,React会在该组件成功加载以后调用该函数一次。
componentDidMount: function() { this.fetchData(); },

现在就可加 fetchData 功能了,这个是我们 app 的最主要的功能了。 该方法用来负责处理取回数据。 您所需要做的就是在执行过那一序列的操作之后调用 this.setState ({movies:data}),这一系列的操作包括React 工作,首先在 setState里面触发再渲染的机制,然后是渲染功能,这时您就会看到 this.state.movies 已经不再是 null了。 我们在这个系列操作的最后调用 done() 方法 —— 一定要确认每次都调用了 done(), 不然的话,所有抛出来的错都会被吞掉了。
 fetchData: function() { fetch(REQUEST_URL) .then((response) => response.json()) .then((responseData) => { this.setState({ movies: responseData.movies, }); }) .done(); },

现在修改  render 方法以去润色一个还没有任何电影资料的加载视图,还有润色第一个电影数据。
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字样,直到我们去请求的数据返回来,然后程序就会开始展示从烂西红柿网站取来的电影数据了。



列表视图


现在我们就可以来修改 render 方法,把我们所有的数据都用列表视图来展示, 这样就不会每次只展示一条电影信息了。

为什么这里选择的是列表视图,还不是把所有的信息都封装好,然后放进滚轮视图?尽管 React  已经够快了,但是要让他封装一个有可能接受无限的列表元素不是会很慢。但是对于列表视图来说,他每次只润色那些够一屏显示的数据,那些没有在当前屏展示的已经被润色过的数据就会从原生视图结构里面给去掉。

最先做的事:添加列表视图到文件的顶部去。
var {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} = React;

现在,修改render 函数,以保证一旦我们拿到了数据就能给我们的电影润色上去。
render: function() { if (!this.state.loaded) { return this.renderLoadingView(); } return ( <ListView dataSource={this.state.dataSource} renderRow={this.renderMovie} style={styles.listView} /> ); },

dataSource 是一个接口, ListView 可以根据这个接口来决定哪一行数据发生了变化,需要调用更新了。

您也许注意到了,我们是从this.date里面调用 dataSource 文件。 下一步就是添加一个空的 dataSource 给对象,然后由 getInitialState 赋值后返回。 同时,我们也把数据存储在了  dataSource 中,应该注意不要再使用  this.state.movies 了,以免存储了数据两次。 在这也要给状态(this.state.loaded)一个布尔值以判断拿取数据的过程是否结束。
 getInitialState: function() { return { dataSource: new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2, }), loaded: false, }; },

这里就是通过更新状态修改后的 fetchData 方法:
 fetchData: function() { fetch(REQUEST_URL) .then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.movies), loaded: true, }); }) .done(); },

最后让我们给列表视图添加好样式代码:
 listView: { paddingTop: 20, backgroundColor: '#F5FCFF', },

您所应该看到最终效果应该是这样的



还有一些工作需要完成以完善这个app 的功能:比方添加导航,搜索,无限滚动加载等。 您可以查看 电影示例,看看这些功能是如何工作的

附:全部代码
/**
 * 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);

不深思则不能造于道。不深思而得者,其得易失。

名人名言- 曾国藩
  • By 优联实达
  • 2015-10-26
  • 1867
  • 公司新闻,网站开发,网站设计,UI