前言
本篇文章的作用在于帮助你快速上手使用react native编写ios应用。如果你现在还不太了解react native是什么以及facebook为什么要创建react native,你可以先看看这篇博客。
阅读本文之前,我们假设你已经有过使用react创建网站的经验。如果你还是一个react新手,那么我们建议你从react的网站开始学习。
设置
使用react native开发ios应用需要osx系统,xcode,homebrew,node,npm以及watchman,你也可以有选择的使用flow。
在安装完这些依赖项目之后,你可以简单的使用两行命令来开启一个react native项目:
npm install -g react-native-cli
react-native-cli是用来开发react native的命令行工具。你需要使用npm来安装它。上面这行代码将会帮助你在terminal中安装react-native命令。当然,你只需要运行一次这行代码。
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_**_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_**_data[0]; return ( {movie.title} {movie.year} ); }
按下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组件上。
按下cmd+r你会发现图片已经渲染出来了。
添加其他样式
很好,我们现在已经把数据渲染出来了。现在我们来让我们的应用变得好看一些。我想把文字放在图片的右侧,同时让标题大一些并居中:
+---------------------------------+|+-------++----------------------+||| || title |||| image || |||| || year |||+-------++----------------------+|+---------------------------------+
我们会添加另一个container,这是为了让我们的组件在外层的组件中垂直居中。
return ( {movie.title} {movie.year} );
现在并没有多少变化,我们在文字外层添加了一个包裹容器并将其放在了图片后面(因为文字要在图片的右边)。现在我们来看看样式会变成什么样:
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/**in_theaters.json';var page_size = 25;var params = '?apikey=' + api_key + '&page_limit=' + page_size;var request_url = api_url + params;
为我们的应用添加初始状态以便我们可以通过检查this.state.** === null来确定电影数据有没有被城管加载。当电影数据返回时,我们可以通过this.setstate({**: **data})来设置数据。将下面的代码添加到render函数之前:
getinitialstate: function() { return { **: null, }; },
我们想要在组件完成加载后发送请求,componentdidmount是react组件中的一个函数,它只会在组件加载完成之后被调用一次。
componentdidmount: function() { this.fetchdata(); },
现在添加组件中会用到的fetchdata函数。这个方法将负责处理数据抓取。你需要做的仅仅是在promise完成解析之后调用this.setstate({**: data}),因为setstate会触发重新渲染,而此时render函数会注意到this.state.**不再是null。注意我们会在promise链的最后调用done()–一定要确保调用done(),否则错误信息可能会被忽略。
fetchdata: function() { fetch(request_url) .then((response) => response.json()) .then((responsedata) => { this.setstate({ **: responsedata.**, }); }) .done(); },
现在修改render函数来渲染一个loading视图,如果电影数据还没有返回的话,否则将渲染第一部电影:
render: function() { if (!this.state.**) { return this.renderloadingview(); } var movie = this.state.**[0]; return this.rendermovie(movie); }, renderloadingview: function() { return ( loading **... ); }, rendermovie: function(movie) { return ( {movie.title} {movie.year} ); },
现在按下cmd+r,你应该已经看到了”loading **…”,直到电影数据返回,接着页面就会渲染第一部从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 ( ); }
datasource是一个listview的接口,作用是决定那些行会被改变。
注意在这里使用datasource而不是this.state。下一步我们需要在getinitialstate的返回对象上添加一个空的datasource,我们不能再使用this.state.**防止数据被存储两次。我们可以使用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.**), loaded: true, }); }) .done(); },
最后,我们在styles中为listview组件添加样式:
listview: { paddingtop: 20, backgroundcolor: '#f5fcff', },
下面是最终的效果图:
接下来我们还可以通过添加导航,搜索,无线滚动加载等等来弯沉一个完整的应用。你可以查看[电影示例](** 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/**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.**), loaded: true, }); }) .done(); }, render: function() { if (!this.state.loaded) { return this.renderloadingview(); } return ( ); }, renderloadingview: function() { return ( loading **... ); }, rendermovie: function(movie) { return ( {movie.title} {movie.year} ); },});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);
来源http://facebook.github.io/react-native/docs/tutorial.htm