React Native新手引导
本教程希望讓您快速熟悉使用React Native來編寫iOS和Android App的技巧。如果你希望知道React Native是什么以及為什么Facebook打造了它,可以讀讀這篇博文
我們這里假設(shè)你已經(jīng)有了使用React編寫Web應(yīng)用程序的經(jīng)驗(yàn)。如果還沒有,建議你可以先從React官網(wǎng)開始學(xué)習(xí)。
準(zhǔn)備工作
React Native需要一些基礎(chǔ)的配置工作,你可以參考開始使用React Native來進(jìn)行。
在所有依賴的軟件都已經(jīng)安裝完畢后,只需要輸入兩條命令就可以創(chuàng)建一個(gè)React Native工程。
npm install -g react-native-cli
react-native-cli是一個(gè)終端命令,它可以完成其余的設(shè)置工作。它可以通過npm安裝。剛才這條命令會往你的終端安裝一個(gè)叫做react-native的命令。這個(gè)安裝過程你只需要進(jìn)行一次。
react-native init AwesomeProject
這個(gè)命令會初始化一個(gè)工程、下載React Native的所有源代碼和依賴包,最后在AwesomePrjoect/iOS/AwesomeProject.xcodeproj和AwesomeProject/android/app下分別創(chuàng)建一個(gè)新的XCode工程和一個(gè)gradle工程。?
譯注:由于眾所周知的網(wǎng)絡(luò)原因,react-native命令行從npm官方源拖代碼時(shí)會遇上麻煩。請先將npm倉庫源替換為國內(nèi)鏡像:?
npm config set registry https://registry.npm.taobao.org npm config set disturl https://npm.taobao.org/dist 另,執(zhí)行init時(shí)切記不要在前面加上sudo(否則新項(xiàng)目的目錄所有者會變?yōu)閞oot而不是當(dāng)前用戶,導(dǎo)致一系列權(quán)限問題,請使用chown修復(fù))。
 本站論壇區(qū)提供了完整的綠色純凈新項(xiàng)目包。完整打包全部iOS和Android的第三方依賴,只要環(huán)境配置正確,無需科學(xué)上網(wǎng)漫長等待,解壓即可直接運(yùn)行。
開發(fā)
想開發(fā)iOS版本,你現(xiàn)在可以在XCode中打開剛剛創(chuàng)建的工程(AwesomePrjoect/iOS/AwesomeProject.xcodeproj),然后只要按下?+R就可以構(gòu)建并運(yùn)行。這個(gè)操作會同時(shí)打開一個(gè)用于實(shí)現(xiàn)動態(tài)代碼加載的Node服務(wù)(React Packager)。所以每當(dāng)你修改代碼,你只需要在模擬器中按下?+R,而無需重新在XCode中編譯。
想開發(fā)Android版本,先連接你的設(shè)備或啟動模擬器,然后在AwesomeProject目錄下運(yùn)行react-native run-android,就會構(gòu)建工程并自動安裝到你的模擬器或者設(shè)備,同時(shí)啟動用于實(shí)現(xiàn)動態(tài)代碼加載的Node服務(wù)。當(dāng)你修改代碼之后,你需要打開搖一搖菜單(搖一下設(shè)備,或者按下設(shè)備的Menu鍵,或者在模擬器上按下F2或Page Up,Genymotion按下?+M),然后在菜單中點(diǎn)擊“Reload JS”。
在本向?qū)е形覀儠?chuàng)建一個(gè)簡單的Movies應(yīng)用,它可以獲取25個(gè)上映中的電影,然后把他們在一個(gè)ListView中顯示。
Hello World
react-native init命令會創(chuàng)建一個(gè)指定名字的應(yīng)用,我們剛才輸入的命令就創(chuàng)建了一個(gè)名為AwesomePrjoect的應(yīng)用。這是一個(gè)簡單的Hello World應(yīng)用。對于iOS版本,你可以編輯index.ios.js來做一些改動,然后在模擬器中按?+R來看到修改的結(jié)果。對Android版本,你可以編輯index.android.js來做一些改動,然后在搖一搖菜單中點(diǎn)擊“Reload JS”來看到修改的結(jié)果。
模擬數(shù)據(jù)
譯注:本文的示例代碼改用了ES6語法,可能和其他文檔寫法不一致。但React Native從0.18之后,新建項(xiàng)目默認(rèn)已經(jīng)采用了ES6語法,故我們推薦不熟悉ES6與ES5區(qū)別的朋友先讀讀這篇文章,另外還可以看看阮一峰老師的書。
在我們真正從Rotten Tomatoes(譯注:一個(gè)國外的電影社區(qū))抓取數(shù)據(jù)之前,我們先制造一些模擬數(shù)據(jù)來練一練手。在Facebook我們通常在JS文件的開頭,緊跟著import語句之后聲明一個(gè)常量,不過這不重要,你可以把它放在index.ios.js和index.android.js的任意位置:
var MOCKED_MOVIES_DATA = [{title: '標(biāo)題', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}}, ];展現(xiàn)一個(gè)電影
我們接下來要展現(xiàn)一個(gè)電影,繪制它的標(biāo)題、年份、以及縮略圖(譯注:這個(gè)過程我們通常會叫做“渲染/render”,后面我們都會用“渲染”這個(gè)詞)。渲染縮略圖需要用到Image組件,所以把Image添加到對React的import列表中。
import React, {AppRegistry,Component,Image,StyleSheet,Text,View, } from 'react-native';然后修改一下render函數(shù),這樣我們可以把上面創(chuàng)建的模擬數(shù)據(jù)渲染出來。
render() {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,現(xiàn)在你應(yīng)該能看到文字"Title"和"2015",但現(xiàn)在Image組件沒渲染任何東西,這是因?yàn)槲覀冞€沒有為圖片指定我們想要渲染的寬和高。這通過樣式來實(shí)現(xiàn)。當(dāng)我們修改樣式的時(shí)候,我們也應(yīng)該清理掉我們不再使用的樣式。
var styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},thumbnail: {width: 53,height: 81,}, });然后把它應(yīng)用到Image組件上:
<Imagesource={{uri: movie.posters.thumbnail}}style={styles.thumbnail}/>按下?+R或者Reload JS,現(xiàn)在圖片應(yīng)該可以被渲染出來了。
添加樣式
現(xiàn)在我們已經(jīng)成功的把我們的數(shù)據(jù)渲染出來了,下面讓我們把它弄的更好看一些。我想把文字放在圖片的右邊,然后把標(biāo)題弄的大一些,并且水平居中:
+---------------------------------+ |+-------++----------------------+| || || 標(biāo)題 || || 圖片 || || || || 年份 || |+-------++----------------------+| +---------------------------------+所以我們需要增加一個(gè)container來實(shí)現(xiàn)一個(gè)水平布局內(nèi)嵌套一個(gè)垂直布局。
return (<View style={styles.container}><Imagesource={{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>);和之前相比并沒有太多變化,我們增加了一個(gè)container來包裝文字,然后把它移到了Image的后面(因?yàn)樗麄冏罱K在圖片的右邊)。然后我們來看看樣式要怎么改:
container: {flex: 1,flexDirection: 'row',justifyContent: 'center',alignItems: 'center',backgroundColor: '#F5FCFF',},我們用Flexbox來布局。如果你想了解更多,可以讀讀這篇文章。
在上面的代碼片段中,我們用了一句flexDirection: 'row'來讓我們的主容器的成員從左到右橫向布局,而非默認(rèn)的從上到下縱向布局。
現(xiàn)在我們往style對象里增加另一個(gè)樣式:
rightContainer: {flex: 1,},這句話的作用是讓rightContainer在父容器中占據(jù)Image之外剩下的全部空間。如果你還不是很理解的話,你可以往rightContainer里增加一個(gè)backgroundColor看一看,然后再去掉flex:1對比一下。你會發(fā)現(xiàn)去掉這一句后,容器會變成能容納它孩子的最小大小。
給文字添加樣式就簡單的多了:
title: {fontSize: 20,marginBottom: 8,textAlign: 'center',},year: {textAlign: 'center',},再按一次?+R或者Reload JS來看看最新的結(jié)果。
拉取真正的數(shù)據(jù)
從Rotten Tomatoes的API拉取數(shù)據(jù)和學(xué)習(xí)React Native并沒有什么直接關(guān)系,所以你也可以直接跳過本節(jié)。
把下面的常量放到文件的最開頭(通常在require下面)來創(chuàng)建我們請求數(shù)據(jù)所需的地址常量REQUEST_URL
/*** 為了避免騷擾,我們用了一個(gè)樣例數(shù)據(jù)來替代Rotten Tomatoes的API* 請求,這個(gè)樣例數(shù)據(jù)放在React Native的Github庫中。*/ var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';首先在應(yīng)用中創(chuàng)建一個(gè)初始的null狀態(tài),這樣可以通過this.state.movies == null來判斷我們的數(shù)據(jù)是不是已經(jīng)被抓取到了。我們在服務(wù)器響應(yīng)返回的時(shí)候執(zhí)行this.setState({movies: moviesData})來改變這個(gè)狀態(tài)。把下面這段代碼放到我們的React類的render函數(shù)之前:
constructor(props) {super(props); //這一句不能省略,照抄即可this.state = {movies: null, //這里放你自己定義的state變量及初始值};}組件加載完畢之后,就可以向服務(wù)器請求數(shù)據(jù)。componentDidMount是React組件的一個(gè)生命周期方法,它會在組件剛加載完成的時(shí)候調(diào)用一次,以后不再會被調(diào)用。React中的各種生命周期方法請參閱此文檔。
componentDidMount() {this.fetchData();}現(xiàn)在我們來為組件添加fetchData函數(shù)。你所需要做的就是在Promise調(diào)用鏈結(jié)束后執(zhí)行this.setState({movies:data})。在React的工作機(jī)制下,setState實(shí)際上會觸發(fā)一次重新渲染的流程,此時(shí)render函數(shù)被觸發(fā),發(fā)現(xiàn)this.state.movies不再是null。注意我們在Promise調(diào)用鏈的最后調(diào)用了done()?—— 這樣可以拋出異常而不是簡單忽略。
fetchData() {fetch(REQUEST_URL).then((response) => response.json()).then((responseData) => {this.setState({movies: responseData.movies,});}).done();}現(xiàn)在我們來修改render函數(shù)。在電影數(shù)據(jù)加載完畢之前,先渲染一個(gè)“加載中”的視圖;而如果電影數(shù)據(jù)已經(jīng)加載完畢了,則渲染第一個(gè)電影數(shù)據(jù)。
render() {if (!this.state.movies) {return this.renderLoadingView();}var movie = this.state.movies[0];return this.renderMovie(movie);}renderLoadingView() {return (<View style={styles.container}><Text>正在加載電影數(shù)據(jù)……</Text></View>);}renderMovie(movie) {return (<View style={styles.container}><Imagesource={{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>);}現(xiàn)在再按一次?+R或者Reload JS,你會首先看到“正在加載電影數(shù)據(jù)……”,然后在響應(yīng)數(shù)據(jù)到達(dá)之后,看到第一個(gè)電影的信息。
ListView
現(xiàn)在我們來讓我們的應(yīng)用能夠渲染所有的數(shù)據(jù)而不是僅僅第一部電影。我們要用到的就是ListView組件。
為什么建議把內(nèi)容放到ListView里?比起直接渲染出所有的元素,或是放到一個(gè)ScrollView里有什么優(yōu)勢?這是因?yàn)楸M管React很高效,渲染一個(gè)可能很大的元素列表還是會很慢。ListView會安排視圖的渲染,只顯示當(dāng)前在屏幕上的那些元素。而那些已經(jīng)渲染好了但移動到了屏幕之外的元素,則會從原生視圖結(jié)構(gòu)中移除(以提高性能)。
首先要做的事情:在文件最開頭,從React中引入ListView。
import React, {AppRegistry,Component,Image,ListView,StyleSheet,Text,View, } from 'react-native';現(xiàn)在來修改render函數(shù)。當(dāng)我們已經(jīng)有了數(shù)據(jù)之后,渲染一個(gè)包含多個(gè)電影信息的ListView,而不僅僅是單個(gè)的電影。
render() {if (!this.state.loaded) {return this.renderLoadingView();}return (<ListViewdataSource={this.state.dataSource}renderRow={this.renderMovie}style={styles.listView}/>);}dataSource接口用來在ListView的整個(gè)更新過程中判斷哪些數(shù)據(jù)行發(fā)生了變化。
你會注意到我們現(xiàn)在用到了this.state中的dataSource。下一步就是在constructor生成的初始狀態(tài)中添加一個(gè)空白的dataSource。另外,我們現(xiàn)在要把數(shù)據(jù)存儲在dataSource中了,所以不再另外用this.state.movies來保存數(shù)據(jù)。我們可以在state里用一個(gè)布爾型的屬性(this.state.loaded)來判斷數(shù)據(jù)加載是否已經(jīng)完成了。
constructor(props) {super(props);this.state = {dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2,}),loaded: false,};}同時(shí)我們也要修改fetchData方法來把數(shù)據(jù)更新到dataSource里:
fetchData() {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',},現(xiàn)在可以體現(xiàn)最終的結(jié)果了:
為了實(shí)現(xiàn)一個(gè)完整功能的應(yīng)用,接下來其實(shí)還有一些工作要做,譬如:添加導(dǎo)航器,搜索,加載更多,等等等等。可以在Movies示例中看看我們做了什么。
最終的代碼
/*** Sample React Native App* https://github.com/facebook/react-native*/import React, {AppRegistry,Component,Image,ListView,StyleSheet,Text,View, } from 'react-native';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;class AwesomeProject extends Component {constructor(props) {super(props);this.state = {dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2,}),loaded: false,};}componentDidMount() {this.fetchData();}fetchData() {fetch(REQUEST_URL).then((response) => response.json()).then((responseData) => {this.setState({dataSource: this.state.dataSource.cloneWithRows(responseData.movies),loaded: true,});}).done();}render() {if (!this.state.loaded) {return this.renderLoadingView();}return (<ListViewdataSource={this.state.dataSource}renderRow={this.renderMovie}style={styles.listView}/>);}renderLoadingView() {return (<View style={styles.container}><Text>Loading movies...</Text></View>);}renderMovie(movie) {return (<View style={styles.container}><Imagesource={{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);本文轉(zhuǎn)自React Native中文網(wǎng):http://reactnative.cn/docs/0.20/tutorial.html#content
總結(jié)
以上是生活随笔為你收集整理的React Native新手引导的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: mysql slave同步_Slave_
- 下一篇: [转载] --- 让线程按顺序执行8种方
