前言
學(xué)習(xí)本系列內(nèi)容需要具備一定 HTML 開發(fā)基礎(chǔ),沒有基礎(chǔ)的朋友可以先轉(zhuǎn)至 HTML快速入門(一) 學(xué)習(xí)
本人接觸 React Native 時(shí)間并不是特別長,所以對其中的內(nèi)容和性質(zhì)了解可能會有所偏差,在學(xué)習(xí)中如果有錯會及時(shí)修改內(nèi)容,也歡迎萬能的朋友們批評指出,謝謝
文章第一版出自簡書,如果出現(xiàn)圖片或頁面顯示問題,煩請轉(zhuǎn)至 簡書 查看 也希望喜歡的朋友可以點(diǎn)贊,謝謝
緊急提醒:
- react-native 0.44 開始 不再支持 Navigatior,可以使用 react-native-Navigation 進(jìn)行替代,具體使用方法過段時(shí)間會推出。
Navigator 與 NavigatorIOS 介紹
開發(fā)中,幾乎所有的APP中或多或少都會涉及到多個(gè)界面間的切換,在React Native中有兩個(gè)組件負(fù)責(zé)實(shí)現(xiàn)這樣的效果 —— Navigator 和 NavigatorIOS
Navigator可以在iOS和Android同時(shí)使用,而NavigatorIOS則是包裝了UIKit庫的導(dǎo)航功能,使用戶可以使用左劃功能來返回到上一界面
Navigator 屬性
官方文檔中是這樣解釋的:使用導(dǎo)航器可以讓你在應(yīng)用的不同場景(頁面)間進(jìn)行切換。導(dǎo)航器通過路由對象來分辨不同的場景。利用renderScene方法,導(dǎo)航欄可以根據(jù)指定的路由來渲染場景
可以通過configureScene屬性獲取指定路由對象的配置信息,從而改變場景的動畫或者手勢。查看Navigator.SceneConfigs來獲取默認(rèn)的動畫和更多的場景配置選項(xiàng)
-
configureScene:可選的函數(shù),用來配置場景動畫和手勢。會帶有兩個(gè)參數(shù)調(diào)用(一個(gè)是當(dāng)前的路由,一個(gè)是當(dāng)前的路由棧)然后它會返回一個(gè)場景配置對象
- Navigator.SceneConfigs.PushFromRight(默認(rèn))
(route, routeStack) => Navigator.SceneConfigs.FloatFromRight效果:
PushFromRight.gif- Navigator.SceneConfigs.FloatFromLeft
(route, routeStack) => Navigator.SceneConfigs.FloatFromLeft效果:
FloatFromLeft.gif- Navigator.SceneConfigs.FloatFromBottom
(route, routeStack) => Navigator.SceneConfigs.FloatFromBottom效果:
FloatFromBottom.gif- Navigator.SceneConfigs.FloatFromBottomAndroid
(route, routeStack) => Navigator.SceneConfigs.FloatFromBottomAndroid效果:
FloatFromBottomAndroid.gif- Navigator.SceneConfigs.FadeAndroid
(route, routeStack) => Navigator.SceneConfigs.FadeAndroid效果:
FadeAndroid.gif- Navigator.SceneConfigs.HorizontalSwipeJump
(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJump效果:
HorizontalSwipeJump.gif- Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJumpFromRight效果:
HorizontalSwipeJumpFromRight.gif- Navigator.SceneConfigs.VerticalUpSwipeJump
(route, routeStack) => Navigator.SceneConfigs.VerticalUpSwipeJump效果:
VerticalUpSwipeJump.gif- Navigator.SceneConfigs.VerticalDownSwipeJump
(route, routeStack) => Navigator.SceneConfigs.VerticalDownSwipeJump效果:
VerticalDownSwipeJump.gif - Navigator.SceneConfigs.PushFromRight(默認(rèn))
initialRoute:定義啟動時(shí)加載的路由。路由是導(dǎo)航欄用來識別渲染場景的一個(gè)對象。
initialRoute必須是initialRouteStack(路由棧)中的一個(gè)路由。initialRoute默認(rèn)為initialRouteStack中最后一項(xiàng)initialRouteStack:提供一個(gè)路由集合用來初始化。如果沒有設(shè)置初始路由的話則必須設(shè)置該屬性。如果沒有提供該屬性,它將被默認(rèn)設(shè)置成一個(gè)只含有
initialRoute的數(shù)組naviagtionBar:可選參數(shù),提供一個(gè)在場景切換的時(shí)候保持的導(dǎo)航欄
navigator:可選參數(shù),提供從父導(dǎo)航器獲得的導(dǎo)航器對象
onDidFocus:每當(dāng)導(dǎo)航切換完成或初始化之后,調(diào)用此回調(diào),參數(shù)為新場景的路由
onWillFocus:會在導(dǎo)航切換之前調(diào)用,參數(shù)為目標(biāo)路由器
-
renderScene:必要參數(shù),用來渲染指定路由的場景。調(diào)用的參數(shù)是路由和導(dǎo)航器
(route, navigator) => <MySceneComponent title={route.title} navigator={navigator} /> sceneStyle:將會應(yīng)用在每個(gè)場景的容器上的樣式
Navigator 方法
- 如果你得到一個(gè)
navigator對象的引用,則可以調(diào)用許多方法來進(jìn)行導(dǎo)航- getCurrentRoutes():獲取當(dāng)前棧里的路由,也就是push進(jìn)來,沒有pop掉的那些
- jumpBack():跳回之前的路由,當(dāng)前前提是保留現(xiàn)在的,還可以再跳回來,會給你保留原樣
- jumpForward():上一個(gè)方法不是盜用之前的路由,用這個(gè)就可以跳回來了
- push(route):跳轉(zhuǎn)到新場景,并且將場景入棧,你可以稍后跳轉(zhuǎn)過去
- pop():跳轉(zhuǎn)回去并且卸載掉當(dāng)前場景
- replace(route):用一個(gè)新的路由替換掉當(dāng)前場景
- replaceAtIndex(route, index):替換掉指定序列的路由場景
- replacePrevious(route):替換掉之前的場景
- resetTO(route):跳轉(zhuǎn)到新場景,并且重置整個(gè)路由棧
- immediatelyResetRouteStack(routeStack):用新的路由數(shù)組來重置路由棧
- popToRoute(route):pop到路由指定的場景,在整個(gè)路由棧中,處于指定場景之后的場景將會被卸載
- popToTop():pop到棧中的第一個(gè)場景,卸載掉所有的其它場景
Navigator 使用
這邊我們先來完成一個(gè)最基本的導(dǎo)航控制器,然后慢慢深入,做一個(gè)完整的導(dǎo)航控制器
-
首先,我們先創(chuàng)建2個(gè)組件(home、Temp)并初始化組件,以供使用
- home組件代碼
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; var Home = React.createClass( { render() { return ( <View style={styles.container}> <Text>點(diǎn)擊跳轉(zhuǎn)</Text> </View> ); } }); var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1, justifyContent:'center', alignItems:'center' }, }); module.exports = Home;- temp組件代碼
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; var Temp = React.createClass( { render() { return ( <View style={styles.container}> <Text>點(diǎn)擊返回</Text> </View> ); } }); var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1, justifyContent:'center', alignItems:'center' }, }); module.exports = Temp; - home組件代碼
-
實(shí)例化
Navigator需要2個(gè)必要的屬性 —— initialRoute 和 renderSence,它們的作用分別是告訴導(dǎo)航器需要渲染的場景、根據(jù)路由描述渲染出來<Navigator style={{flex: 1}} // 布局 initialRoute={{ name:'Home', // 名稱 component:Home // 要跳轉(zhuǎn)的板塊 }} renderScene={(route, navigator) => { // 將板塊生成具體的組件 let Component = route.component; // 獲取路由內(nèi)的板塊 return <Component {...route.params} navigator={navigator} /> // 根據(jù)板塊生成具體組件 }} /> -
實(shí)際上這樣我們的導(dǎo)航器就已經(jīng)創(chuàng)建完畢了,但是從界面上我們看不到導(dǎo)航欄,但是已經(jīng)具備導(dǎo)航功能,我們分別進(jìn)到
home和temp文件中修改代碼,看看是否真的已經(jīng)實(shí)現(xiàn)導(dǎo)航功能- home代碼
// 引入外部文件 var Temp = require('./Temp'); var Home = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp })}} > <Text>點(diǎn)擊跳轉(zhuǎn)</Text> </TouchableOpacity> </View> ); } });- temp代碼
var Temp = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.pop()}} > <Text>臨時(shí)頁面</Text> </TouchableOpacity> </View> ); } });效果:
navigator基本效果.gif - home代碼
-
在這里,我們還可以為導(dǎo)航器設(shè)置轉(zhuǎn)場動畫,以滿足我們開發(fā)需求,官方已經(jīng)提供了幾種常用的轉(zhuǎn)場動畫,這邊我們就取其中一種作示例,其它的效果可以參考上面的案例
<Navigator initialRoute={{ name:'Home', // 名稱 component:Home // 要跳轉(zhuǎn)的板塊 }} configureScene={(route) => { // 跳轉(zhuǎn)動畫 return Navigator.SceneConfigs.FloatFromBottom; }} style={{flex: 1}} renderScene={(route, navigator) => { // 將板塊生成具體的組件 let Component = route.component; // 獲取路由內(nèi)的板塊 return <Component {...route.params} navigator={navigator} /> // 根據(jù)板塊生成具體組件 }} />效果:
navigator轉(zhuǎn)場動畫.gif -
為了讓導(dǎo)航欄更加人性化,我們可以自己定制導(dǎo)航欄的樣式,定制方式和我們自定義界面一樣,只不過將按鈕的響應(yīng)改為導(dǎo)航欄對應(yīng)的功能即可,這邊就以最基本的導(dǎo)航欄樣式為例
- 視圖部分
var Home = React.createClass( { render() { return ( <View style={styles.container}> {/* 實(shí)例化導(dǎo)航欄 */} {this.setupNavBar()} </View> ); }, setupNavBar(){ return( <View style={styles.navBarStyle}> {/* 左邊按鈕 */} <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp })}} > <Text style={styles.leftButtonTitleStyle}>按鈕</Text> </TouchableOpacity> {/* 中間標(biāo)題 */} <Text style={styles.navBarTitleStyle}>標(biāo)題</Text> {/* 右邊按鈕 */} <TouchableOpacity> <Text style={styles.rightButtonTitleStyle}>按鈕</Text> </TouchableOpacity> </View> ) } });- 樣式部分
var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1 }, navBarStyle: { // 尺寸 width:width, // 當(dāng)前系統(tǒng)為iOS時(shí),導(dǎo)航欄高度為64 height:Platform.OS === 'ios' ? 64 : 44, // 背景顏色 backgroundColor:'rgba(255, 255, 255, 0.9)', // 底部分隔線 borderBottomWidth:0.5, borderBottomColor:'gray', // 主軸方向 flexDirection:'row', // 對齊方式 alignItems:'center', justifyContent:'space-between', // 當(dāng)前系統(tǒng)為iOS時(shí),下次移動15 paddingTop:Platform.OS === 'ios' ? 15 : 0 }, leftButtonTitleStyle: { // 字體大小 fontSize:15, // 字體顏色 color:'blue', // 內(nèi)邊距 paddingLeft:8 }, navBarTitleStyle: { // 字體大小 fontSize:17, // 字體顏色 color:'black' }, rightButtonTitleStyle: { // 字體大小 fontSize:15, // 字體顏色 color:'blue', // 內(nèi)邊距 paddingRight:8 } });iOS運(yùn)行效果:
iOS運(yùn)行效果.gif - 視圖部分
Android運(yùn)行效果:

NavigatorIOS 屬性
-
barTintColor:導(dǎo)航條的背景顏色
barTintColor='red' // 導(dǎo)航欄背景顏色效果:
導(dǎo)航欄背景色 initialRoute( {component: function, title: string, passProps: object, backButtonIcon: Image.propTypes.source, backButtonTitle: string, leftButtonIcon: Image.propTypes.source, leftButtonTitle: string, onLeftButtonPress: function, rightButtonIcon: Image.propTypes.source, rightButtonTitle: string, onRightButtonPress: function, wrapperStyle: [object Object]} ): 使用“路由”對象來包含要渲染的子視圖、它們的屬性、已經(jīng)導(dǎo)航條配置?!皃ush”和任何其他的導(dǎo)航函數(shù)的參數(shù)都是這樣的路由對象(下面實(shí)例模塊會詳細(xì)講解)
itemWrapperStyle:導(dǎo)航器中的組件的默認(rèn)屬性。一個(gè)常見的用途是設(shè)置所有頁面的背景顏色
-
navigationBarHidden:布爾值,決定導(dǎo)航欄是否隱藏
navigationBarHidden={true} // 隱藏導(dǎo)航欄效果:
navigationBarHidden.gif -
shadowHidden:布爾值,決定是否要隱藏1像素的陰影
shadowHidden={true} // 隱藏導(dǎo)航欄下面的陰影效果:
隱藏陰影 -
tintColor:導(dǎo)航欄上按鈕的顏色
tintColor='orange' // 按鈕的顏色效果:
導(dǎo)航欄按鈕顏色 -
titleTextColor:導(dǎo)航器標(biāo)題的文字顏色
titleTextColor='green' // 導(dǎo)航欄標(biāo)題的文字顏色效果:
導(dǎo)航欄標(biāo)題顏色 -
translucent:布爾值,決定導(dǎo)航條是否半透明(注:當(dāng)不半透明時(shí)頁面會向下移動導(dǎo)航欄等高的距離,以防止內(nèi)容被遮蓋)
translucent={false} // 決定導(dǎo)航欄是否半透明(注:當(dāng)不半透明時(shí)頁面會向下移動導(dǎo)航欄等高的距離,以防止內(nèi)容被遮蓋)效果:
導(dǎo)航欄不半透明效果 -
interactivePopGestureEnabled:決定是否啟用滑動返回手勢。不指定此屬性時(shí),手勢會根據(jù)
navigationBar的顯隱情況決定是否啟用(顯示時(shí)啟用手勢,隱藏時(shí)禁用手勢),指定此屬性后,手勢與navigationBar的顯隱情況無關(guān)interactivePopGestureEnabled={false} // 決定是否啟用滑動返回手勢效果:
interactivePopGestureEnabled關(guān)閉.gif
NavigatorIOS 方法
push(route):導(dǎo)航器跳轉(zhuǎn)到一個(gè)新的路由
pop():回到上一頁
popN():回到N頁之前。當(dāng) N=1 的時(shí)候,效果和 pop() 一樣
replace(route):替換當(dāng)前頁的路由,并立即加載新路由的視圖
replacePrevious(route):替換上一頁的路由/視圖
replacePreviousAndPop(route):替換上一頁的路由/視圖并且立即切換回上一頁
resetTO(route):替換最頂級的路由并且回到它
replaceAtIndex:替換指定路由
popToRoute(route):一直回到某個(gè)指定的路由
popToTop():回到最頂層的路由
NavigatorIOS 使用
先來看看怎么使用
NavigatorIOS,我們需要給他指定一個(gè)路由,這樣它才能知道顯示哪個(gè)頁面-
和
Navigator一樣NavigatorIOS需要有個(gè)根視圖來完成初始化,所以我們需要先創(chuàng)建一個(gè)組件來描述這個(gè)界面,并將這個(gè)組件通過路由的形式告訴NavigatorIOS,這樣就可以將這個(gè)界面展示出來-
首先,創(chuàng)建一個(gè)
Home組件,用來作為NavigatorIOS的根視圖- 視圖部分
var Home = React.createClass( { render() { return ( <View style={styles.container}> <Text>點(diǎn)擊跳轉(zhuǎn)頁面</Text> </View> ); } });- 樣式部分
var styles = StyleSheet.create({ container: { // 背景顏色 backgroundColor:'yellow', flex:1, // 對齊方式 justifyContent:'center', alignItems:'center' }, }); - 視圖部分
接著我們在
index.ios.js內(nèi)獲得Home文件
// 引用外部文件 var Home = require('./home');- 然后我們實(shí)例化一個(gè)
NavigatorIOS并設(shè)置路由
var navigatorDemo = React.createClass( { render() { return ( <NavigatorIOS initialRoute={{ component: Home, // 要跳轉(zhuǎn)的頁面 title:'首頁' // 跳轉(zhuǎn)頁面導(dǎo)航欄標(biāo)題 }} style={{flex:1}} // 此項(xiàng)不設(shè)置,創(chuàng)建的導(dǎo)航控制器只能看見導(dǎo)航條而看不到界面 /> ); } });效果:
基本效果 -
-
這樣我們就完成了基本的導(dǎo)航控制器了,那么怎么進(jìn)行跳轉(zhuǎn)呢?其實(shí)也很簡單,官方提供的方法內(nèi)有多種供我們選擇(具體參考上面的方法一欄)
- 這邊我們就來實(shí)現(xiàn)最簡單的跳轉(zhuǎn)和返回,我們使用
TouchableOpacity讓Home中的 <Text>標(biāo)簽 擁有接收事件的能力,并且當(dāng)點(diǎn)擊的時(shí)候通過調(diào)用props來獲取 navigator,并傳遞給他一個(gè)路由,使其知道跳轉(zhuǎn)到哪個(gè)頁面
var Home = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp, // 需要跳轉(zhuǎn)的頁面 title:'跳轉(zhuǎn)的界面' // 跳轉(zhuǎn)頁面導(dǎo)航欄標(biāo)題 })}} > <Text>點(diǎn)擊跳轉(zhuǎn)頁面</Text> </TouchableOpacity> </View> ); } });效果:
NavigatorIOS效果.gif - 這邊我們就來實(shí)現(xiàn)最簡單的跳轉(zhuǎn)和返回,我們使用
-
這邊順便來看下導(dǎo)航欄左右兩邊的按鈕怎么設(shè)置,并且響應(yīng)點(diǎn)擊事件
initialRoute={{ component: Home, // 要跳轉(zhuǎn)的頁面 title:'首頁', // 跳轉(zhuǎn)頁面導(dǎo)航欄標(biāo)題 leftButtonTitle:'左邊', // 實(shí)例化左邊按鈕 onLeftButtonPress:() => {alert('左邊')}, // 左邊按鈕點(diǎn)擊事件 rightButtonTitle:'右邊', // 實(shí)例化右邊按鈕 onRightButtonPress:() => {alert('右邊')} // 右邊按鈕點(diǎn)擊事件 }}效果:
NavigatorIOS左右按鈕.gif 當(dāng)然圖片設(shè)置的方式也是一樣的,只需要調(diào)用
leftButtonIcon和 'rightButtonIcon` 即可(和TabBarIOS一樣,只支持本地圖片)
補(bǔ)充
- props:組件中的props是一種父級向子級傳遞數(shù)據(jù)的方式
- this.props 對象的屬性與組件的屬性一一對應(yīng),但是有一個(gè)例外,就是
this.props.children屬性。它表示組件的所有子節(jié)點(diǎn) - 它里面包含了所有的屬性,所以上面我們在別的文件中可以通過
this.props.navigator的方式獲取navigator
- this.props 對象的屬性與組件的屬性一一對應(yīng),但是有一個(gè)例外,就是
- state:
state是React中組件的一個(gè)對象.React把用戶界面當(dāng)做是狀態(tài)機(jī),想象它有不同的狀態(tài)然后渲染這些狀態(tài),可以輕松讓用戶界面與數(shù)據(jù)保持一致。React中,更新組件的state,會導(dǎo)致重新渲染用戶界面(不要操作DOM).簡單來說,就是用戶界面會隨著state變化而變化- 原理:常用的通知React數(shù)據(jù)變化的方法是調(diào)用
setState(data,callback).這個(gè)方法會合并data到this.state,并重新渲染組件.渲染完成后,調(diào)用可選的。callback回調(diào).大部分情況不需要提供callback,因?yàn)镽eact會負(fù)責(zé)吧界面更新到最新狀態(tài) - 哪些組件應(yīng)該有 state
- 大部分組件的工作應(yīng)該是從
props里取數(shù)據(jù)并渲染出來.但是,有時(shí)需要對用戶輸入,服務(wù)器請求或者時(shí)間變化等作出響應(yīng),這時(shí)才需要state - 組件應(yīng)該盡可能的無狀態(tài)化,這樣能隔離state,把它放到最合理的地方(Redux做的就是這個(gè)事情?),也能減少冗余并易于解釋程序運(yùn)作過程
- 常用的模式就是創(chuàng)建多個(gè)只負(fù)責(zé)渲染數(shù)據(jù)的無狀態(tài)(stateless)組件,在他們的上層創(chuàng)建一個(gè)有狀態(tài)(stateful)組件并把它的狀態(tài)通過props
- 傳給子級.有狀態(tài)的組件封裝了所有的用戶交互邏輯,而這些無狀態(tài)組件只負(fù)責(zé)聲明式地渲染數(shù)據(jù)
- 大部分組件的工作應(yīng)該是從
- 哪些應(yīng)該作為 state
-
state應(yīng)該包括那些可能被組件的事件處理器改變并觸發(fā)用戶界面更新的數(shù)據(jù).這中數(shù)據(jù)一般很小且能被JSON序列化.當(dāng)創(chuàng)建一個(gè)狀態(tài)化的組件的時(shí)候,應(yīng)該保持?jǐn)?shù)據(jù)的精簡,然后存入this.state。 在render()中在根據(jù)state來計(jì)算需要的其他數(shù)據(jù).因?yàn)槿绻趕tate里添加冗余數(shù)據(jù)或計(jì)算所得數(shù)據(jù),經(jīng)常需要手動保持?jǐn)?shù)據(jù)同步
-
- 哪些不應(yīng)該作為 state
-
this.state應(yīng)該僅包括能表示用戶界面狀態(tài)所需要的最少數(shù)據(jù).因此,不應(yīng)該包括:- 計(jì)算所得數(shù)據(jù):
- React組件:在render()里使用props和state來創(chuàng)建它
- 基于props的重復(fù)數(shù)據(jù):盡可能保持用props來做作為唯一的數(shù)據(jù)來源.把props保存到state中的有效的場景是需要知道它以前的值得時(shí)候,因?yàn)槲磥淼膒rops可能會變化
- 計(jì)算所得數(shù)據(jù):
-
- 原理:常用的通知React數(shù)據(jù)變化的方法是調(diào)用





















