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