React Native ios开发第一课

前言

本篇文章的作用在于帮助你快速上手使用React Native编写iOS应用。如果你现在还不太了解React Native是什么以及Facebook为什么要创建React Native,你可以先看看这篇博客

阅读本文之前,我们假设你已经有过使用React创建网站的经验。如果你还是一个React新手,那么我们建议你从React的网站开始学习。

设置

使用React Native开发iOS应用需要OSX系统,Xcode,Homebrew,node,npm以及watchman,你也可以有选择的使用Flow。

在安装完这些依赖项目之后,你可以简单的使用两行命令来开启一个React Native项目:

  1. npm install -g react-native-cli

react-native-cli是用来开发React Native的命令行工具。你需要使用npm来安装它。上面这行代码将会帮助你在terminal中安装react-native命令。当然,你只需要运行一次这行代码。

  1. react-native init AwsomeProject

这行代码可以获取所有React Native的源码以及依赖项,同时会创建一个叫做AwsomeProject/AwsomeProject.xcodeproj的全新Xcode项目。

开发

现在你可以在Xcode中开发这个新项目(AwsomeProject/AwsomeProject.xcodeproj),并简单的使用cmd+R来运行它。运行代码的同时也会自动开启一个node服务器来实现代码的热重载。这样一来你就可以通过cmd+R来查看变化而不需要每次都在Xcode中进行重编译。

在本文中我们将创建一个简单的电影应用,这个应用将抓取目前正在上映的最新的25部电影,并将它们展示在一个ListView中。

Hello World

react-native
init
会复制Example/SampleProject中的内容到你命名的项目中,在本文中项目名称为AwsomeProject。这是一个简单的hello
world应用。你可以通过编辑index.os.js来改变这个应用,然后使用cmd+R在模拟器中查看变化。

伪造数据

在我们开始编写代码从Rotten Tomatoes网站抓取数据之前,我们先来伪造一些数据以便我们可以马上体验一下React Native。在Facebook我们一般会在JS文件的顶部声明常量,并在后面使用,但是随便你加在哪里都好。在index.ios.js中添加以下代码:

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

渲染一部电影

我们会渲染电影标题,年份以及电影海报略缩图。由于略缩图在React Native中是一个Image组件,我们需要将Imagei到React的依赖项中。

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

现在我们修改render函数以便我们可以将上面渲染上面的数据而不仅仅是渲染一个hello world:

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

按下cmd+R你应该在”2015”上面看到”Title”。注意此时Image什么都不会渲染。这是因为我们还没有指定想要的宽度和高度。这需要通过styles属性来设置。在我们修改styles的同时我们还需要把那些不再会使用的样式删除:

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}
 />

按下cmd+R你会发现图片已经渲染出来了。

添加其他样式

很好,我们现在已经把数据渲染出来了。现在我们来让我们的应用变得好看一些。我想把文字放在图片的右侧,同时让标题大一些并居中:

+---------------------------------+
|+-------++----------------------+|
||       ||        Title         ||
|| Image ||                      ||
||       ||        Year          ||
|+-------++----------------------+|
+---------------------------------+

我们会添加另一个container,这是为了让我们的组件在外层的组件中垂直居中。

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',
  },

我们在这里使用弹性盒模型来布局,如果你不熟悉弹性盒模型,可以看看这个教程

在上面的代码中,我们简单的添加了flexDirection:
'row'
来确保我们的main container是水平布局而不是垂直布局。

现在我们添加另一组样式:

 rightContainer: {
    flex: 1,
  },

上面代码的意思是rightContainer会占据外层容器右边的空间,左边则是图片。如果没有看出效果,可以为rightContainer添加一个backgroundColor属性,同时移除flex:
1
。你会看到外出容器的体积会变得劲量的小来适应子容器。

而文本的样式很直观:

title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },

继续按下cmd+R来查看更新之后的视图:

抓取真实数据

从Rotten Tomatoes的API抓取数据和学习React Native并没有多少关系,所以你可以风轻云淡的跳过这一节。

将下面的常量放在文件的顶部来创建一个请求数据使用的REQUEST_URL:

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;

为我们的应用添加初始状态以便我们可以通过检查this.state.movies
===
null
来确定电影数据有没有被城管加载。当电影数据返回时,我们可以通过this.setState({movies:
moviesData})
来设置数据。将下面的代码添加到render函数之前:

getInitialState: function() {
    return {
      movies: null,
    };
  },

我们想要在组件完成加载后发送请求,componentDidMount是React组件中的一个函数,它只会在组件加载完成之后被调用一次。

componentDidMount: function() {
    this.fetchData();
  },

现在添加组件中会用到的fetchData函数。这个方法将负责处理数据抓取。你需要做的仅仅是在promise完成解析之后调用this.setState({movies:
data})
,因为setState会触发重新渲染,而此时render函数会注意到this.state.movies不再是null。注意我们会在promise链的最后调用done()–一定要确保调用done(),否则错误信息可能会被忽略。

fetchData: function() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          movies: responseData.movies,
        });
      })
      .done();
  },

现在修改render函数来渲染一个loading视图,如果电影数据还没有返回的话,否则将渲染第一部电影:

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

现在按下cmd+R,你应该已经看到了”Loading
movies…”,直到电影数据返回,接着页面就会渲染第一部从Rotten Tomatoes抓回来的电影:

ListView

现在我们来修改应用来将所有的数据渲染在一个ListView组件种,而不是只渲染一部电影。

为什么使用ListView要比把所有数据放在一个ScrollView里面好呢?虽然React速度很快,但是渲染一个可能是无限长的列表依然可能很慢。ListView会自动渲染视线之内的视图,而那些在屏幕之外的视图会被暂时移除。

第一件事:在文件的最上方添加ListView

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

现在修改render函数以便一旦我们的数据返回沃恩就可以在一个ListView里面渲染数据:

render: function() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderMovie}
        style={styles.listView}
      />
    );
  }

DataSource是一个ListView的接口,作用是决定那些行会被改变。

注意在这里使用dataSource而不是this.state。下一步我们需要在getInitialState的返回对象上添加一个空的dataSource,我们不能再使用this.state.movies防止数据被存储两次。我们可以使用state的布尔值属性(this.state.loaded)来判断数据抓取是否结束:

getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  },

在这里我们还需要修改fetchData方法来更新state:

fetchData: function() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
          loaded: true,
        });
      })
      .done();
  },

最后,我们在styles中为ListView组件添加样式:

 listView: {
    paddingTop: 20,
    backgroundColor: '#F5FCFF',
  },

下面是最终的效果图:

接下来我们还可以通过添加导航,搜索,无线滚动加载等等来弯沉一个完整的应用。你可以查看[电影示例](Movies Example)来查看完整的代码。

完整的源码

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


时间: 2024-12-26 18:17:40

React Native ios开发第一课的相关文章

《React Native移动开发实战》出版啦

对不起,我来晚了 首先要感谢支持和关注我的朋友,感谢人邮的赵老师,还有公司的领导和同事,他们在我写作的过程中给了很多有用的信息,也给了很多有用的建议,为本书的写作提供了很大帮助.感谢,再次感谢!!! 工作6年多以来,一直想写一本自己的书,一方面是对自己工作经历的一个总结,也是对希望写一本书给曾经的自己一个交代,毕竟30岁的人了,搞不了几年的技术了. 我一直有写博客的习惯,喜欢将自己工作和生活的点滴写成博客,分享给大家.说说我写书的缘由吧,最近几年,特别是15年和16年,我朋友圈的好友陆续出了自己

React Native移动开发资料库

本文整理了React Native开发中额优秀博客,以及优秀的Github库列表(很多英文资料源自于awesome-react-native) 关于开源库类 Star 100+ ️ Star 200+ ️️ Star 500+ ️️️ Star 1000+ ️️️️ Star 2000+ ️️️️️ 关于博客和视频类 值得读读 ️ 建议阅读 ️️ 强烈推荐 ️️️ 目录 网址 完整开源项目 Libraries (Star 100+) 中文博客 英文博客 视频资料 网址 源代码 官方文档 reac

《React Native移动开发实战》一一1.3 搭建React Native开发环境

1.3 搭建React Native开发环境 "磨刀不误砍柴工",在正式开发React Native应用之前,需要先搭建好React Native的开发环境.搭建React Native开发环境有以下几个主要步骤. 原生开发工具:iOS开发使用Xcode,Android开发使用Android Studio and SDK Tools. Node.js(https://nodejs.org/):React Native是借助Node.js,即JavaScript运行时来创建JavaScr

《React Native移动开发实战》一一1.1 看透React Native

1.1 看透React Native React Native(http://facebook.github.io/react-native/)第一次进入公众的视野是在2015年1月的React.js Conf(http://conf.reactjs. org/)上,随后,同年5月份,Facebook在F8 Conference(https://www. fbf8.com/)上正式宣布:React Native项目(如图1.1所示) 在Github开源.结果一天之内,就收获了5000多颗星,受关

《React Native移动开发实战》一一1.2 React Native的特点

1.2 React Native的特点 那么,作为跨平台应用开发的"新贵",React Native相比其他跨平台技术到底有哪些优势呢?1.2.1 其一:Learn Once, Write Anywhere 这句话是React Native官网(http://facebook.github.io/react-native/)对React Native的概述,简单明了地概括了React Native的最大特点和优点. 只需要学习React Native这一种开发方式(包括平台.语言和开发

《React Native移动开发实战》一一3.1 创建新的电商App

3.1 创建新的电商App之前创建了一个简单的电商项目,本节来实现对该项目的重构.3.1.1 移植旧电商项目(1)先创建React Native项目并安装依赖包. react-native init ch04 // 新建React Native项目ch04cd ch04npm install // 或者使用cnpm安装:cnpm install?小知识:npm install命令还可以简写成npm i,更多说明可以使用npm help install查看帮助文档.(2)将第2章ch03项目中的i

React Native iOS环境搭建

感觉React Native会越来越多的公司开始研究.使用.所以周六也抽空搭建了iOS的开发环境,以便以后利用空闲的时间能够学习一下. 废话不多说了,下面简单的列出步骤吧. 1 . 安装Homebrew Homebrew主要用于安装后面需要安装的watchman.flow 打开MAC的终端,输入如下命令: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 经

《React Native移动开发实战》一一1.5 小试牛刀——更改React Native项目源码

1.5 小试牛刀--更改React Native项目源码 应用虽然已经运行起来了,但是到现在还没有看到或修改任何代码,读者是不是觉得意犹未尽呢?下面就来看看React Native项目的源码吧. 打开index.ios.js文件,可以看到与显示在设备上内容直接相关的代码: 01 export default class ch02 extends Component { // 每个页面可以理解成一个组件 02 render() { // 渲染页面的函数 03 return ( 04 // 页面根V

《React Native移动开发实战》一一导读

前言 随着手机和移动互联网技术的日益成熟,移动应用的领域也从如何开发,发展到如何更高效.更低成本地开发.传统的原生平台(iOS.Android)开发技术虽然比较成熟,但由于开发效率和成本的限制,已经越来越无法满足移动互联网应用的开发需求. 所以,具有简单.迅速.跨平台的优势,而且基于Web开发语言和布局技术的React Native得以迅速流行,并一举夺得跨平台开发技术的头筹. 目前市场上大多数React Native书籍主要以翻译和讲解官方文档为主,并未从开发实际应用出发,通过典型案例来指导读