接觸了RN之后啡莉,必不可免得要接觸界面之間跳轉(zhuǎn)之類的需求逃默,而這一類需求的實(shí)現(xiàn)必須要使用到Navigator這個(gè)導(dǎo)航器攀圈,這次記錄一下使用過程中對(duì)于Navigator導(dǎo)航器的認(rèn)知。
首先要理解這個(gè)導(dǎo)航器凛篙,可以通俗的理解和Android中activity的堆棧管理一樣黍匾,導(dǎo)航器除了界面導(dǎo)航功能之外,還提供界面棧的管理呛梆,界面的跳入和跳出锐涯。(RN中每一個(gè)component都相當(dāng)于一個(gè)組件,一個(gè)或多個(gè)component共同構(gòu)成場(chǎng)景(Scene)填物,場(chǎng)景通俗的理解就是一個(gè)占據(jù)整個(gè)屏幕的界面)
RN中的入口是index.android.js(以Android為例)纹腌,這個(gè)index.js可以看成整個(gè)RN組建的框架,一些基礎(chǔ)的東西都在這里實(shí)例化滞磺、定義升薯。我們的導(dǎo)航器也需要在這個(gè)文件中被創(chuàng)建。(后文中提到的BackAndroid也在這里面定義)
Navigator屬性介紹
Navigator中包含如下屬性:
initialRoute
初始化路由击困,初始化需要顯示的Component涎劈,其中的component參數(shù)必須要有,定義如下:
initialRoute={{title:'main',id:'main',component:defaultComponent}}
configureScene
配置場(chǎng)景動(dòng)畫阅茶,系統(tǒng)提供了很多動(dòng)畫蛛枚,如從底部彈出,從左彈出等目派,參數(shù)如下:
組件中定義:
configureScene={this._configureScence.bind(this)}
_configureScence(route) {
console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)
if(route.type =='Bottom') {
returnNavigator.SceneConfigs.FloatFromBottom;// 底部彈出
}else if(route.type =='Left') {
returnNavigator.SceneConfigs.FloatFromLeft// 右側(cè)彈出
}else if(route.type =='Right') {
returnNavigator.SceneConfigs.FloatFromRight//左側(cè)彈出
}
returnNavigator.SceneConfigs.PushFromRight;// 默認(rèn)右側(cè)彈出
}
renderScene
場(chǎng)景渲染坤候,根據(jù)路由來確定要掛載渲染的場(chǎng)景胁赢,設(shè)置如下:
組件中定義
renderScene={this._scene.bind(this)}
//場(chǎng)景渲染方法企蹭,傳入路由器和導(dǎo)航器兩個(gè)方法
_scene(route, navigator) {
console.log(route)
//這個(gè)里面如果不做處理,默認(rèn)返回的是initialRoute初始化的component
letComponent= route.component;
//路由器的params可以攜帶參數(shù)
//將改導(dǎo)航器傳遞給下一個(gè)Component
return
//或者直接引入一個(gè)現(xiàn)成的Component
//return
}
ref
這個(gè)屬性有點(diǎn)很微妙智末,網(wǎng)上很多介紹Navigator的博客代碼中沒有寫這個(gè)屬性谅摄,這個(gè)屬性相當(dāng)于給一個(gè)組件添加一個(gè)label標(biāo)簽,然后通過該標(biāo)簽可以找到對(duì)應(yīng)的組件系馆,發(fā)現(xiàn)這個(gè)屬性的原因是我在寫B(tài)ackAndroid的時(shí)候送漠,需要使用到navigator這個(gè)對(duì)象,在監(jiān)聽物理返回鍵的時(shí)候判斷是否還有路由存在由蘑,通常在方法中獲取navigator的方法如下:
_pressButton(){
const {navigator} = this.props;
}
這么寫的前提是_pressButton該方法一般都會(huì)被bind闽寡,而且該Component在掛載前已經(jīng)把navigator傳遞過來了代兵,所以可以獲得到,但是我們?cè)趇ndex.js中使用BackAndroid爷狈,定義方法不管是使用箭頭函數(shù)或者在構(gòu)造方法中bind對(duì)應(yīng)的方法植影,這個(gè)時(shí)候this.props都沒有navigator這個(gè)屬性,所以這個(gè)時(shí)候是找不到的涎永,也就沒辦法實(shí)現(xiàn)導(dǎo)航回退的功能思币,而使用ref就很好的解決這個(gè)問題了,即子組件獲取父組件通過props羡微、父組件獲取子組件通過refs谷饿。如下設(shè)置:
在組建中添加:
ref="navigator"
方法中調(diào)用:
onBackAndroid=()=>{
constnavigator=this.refs.navigator;
...
}
Navigator方法
getCurrentRoutes() - 獲取當(dāng)前棧里的路由列表,也就是push進(jìn)來妈倔,沒有pop掉的那些
jumpBack() - 跳回之前的路由博投,保留現(xiàn)在的,還可以再跳回來盯蝴。相當(dāng)于瀏覽器的回退
jumpForward() - 結(jié)合jumpBack贬堵,此方法再重新打開回退前的,相當(dāng)于瀏覽器的前進(jìn)
jumpTo(route) - 跳轉(zhuǎn)到一個(gè)沒有被取消掛載的已存在場(chǎng)景
push(route) - push一個(gè)新的路由場(chǎng)景
pop() - 移除并取消掛載當(dāng)前的場(chǎng)景结洼,回到上一個(gè)場(chǎng)景
replace(route) -用一個(gè)新的路由場(chǎng)景替代當(dāng)前的場(chǎng)景黎做,該方法之后當(dāng)前的場(chǎng)景就被取消掛載了
replaceAtIndex(route,index) -通過制定index下標(biāo)replace
replacePrevious(route) -replace前一個(gè)場(chǎng)景
immediatelyResetRouteStack(routeStack) -用新的路由場(chǎng)景Stack重置堆棧中的每一個(gè)場(chǎng)景
popToRoute(route) - 移除并取消掛載當(dāng)前場(chǎng)景到制定場(chǎng)景之間的對(duì)
popToTop() - 移除并取消掛載出堆棧中第一個(gè)場(chǎng)景外的其他場(chǎng)景
其中route路由最基本的就是
var route = {component: LoginComponent}
完整代碼如下
//component是從react中來的
importReact, {Component}from'react';
//Text以及View等都是從react-native中來的
import{
AppRegistry,
StyleSheet,
Navigator,
BackAndroid,
Dimensions
}from'react-native';
importsplashfrom'./app/mainview/splash'
importguidefrom'./app/mainview/guide'
//定義一個(gè)Component,按照ES6的語法來,就和java語法中定義class一樣松忍,繼承component
export? default? classAndroidTestComponentextendsComponent{
//構(gòu)造函數(shù)
constructor(props) {
super(props)
//如果_onBackAndroid不是一個(gè)箭頭函數(shù)蒸殿,需要在構(gòu)造函數(shù)中bind this,要不然在添加監(jiān)聽和移除監(jiān)聽時(shí)操作的對(duì)象是不同的
// this._onBackAndroid = this.onBackAndroid.bind(this)
}
//場(chǎng)景動(dòng)畫
_configureScence(route) {
console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)
if(route.type =='Bottom') {
returnNavigator.SceneConfigs.FloatFromBottom;// 底部彈出
}else if(route.type =='Left') {
returnNavigator.SceneConfigs.FloatFromLeft// 右側(cè)彈出
}else if(route.type =='Right') {
returnNavigator.SceneConfigs.FloatFromRight//左側(cè)彈出
}
returnNavigator.SceneConfigs.PushFromRight;// 默認(rèn)右側(cè)彈出
}
//場(chǎng)景渲染
_scene(route, navigator) {
letComponent= route.component;
//傳遞參數(shù)以及導(dǎo)航器
return
}
//使用箭頭函數(shù)鸣峭,直接綁定this宏所,不需要再構(gòu)造函數(shù)中再去bind
onBackAndroid=()=>{
//使用refs來獲取導(dǎo)航器
constnavigator=this.refs.navigator;
if(!navigator){
return false;
}
constrouters=navigator.getCurrentRoutes();
if(routers.length>1){
navigator.pop();
return true;
}else{
return false;
}
}
//compoment將要掛載的函數(shù),這個(gè)時(shí)候可以在繼續(xù)更新state 添加監(jiān)聽
componentWillMount() {
console.log("AndroidTestComponent=====componentWillMount")
BackAndroid.addEventListener('hardwareBackPress',this.onBackAndroid)
}
//render屬性對(duì)應(yīng)的函數(shù)會(huì)返回一段JSX來表示該組件的結(jié)構(gòu)和布局。該部分是一個(gè)組件必不可少的地方摊溶,沒有這些內(nèi)容爬骤,就無法構(gòu)成一個(gè)組件。
//render方法必須返回單個(gè)根元素
//compoment掛載渲染的函數(shù)
render() {
//定義默認(rèn)閃屏界面
letdefaultComponent= splash;
return(
configureScene={this._configureScence.bind(this)}
renderScene={this._scene.bind(this)}
ref="navigator"
/>
);
}
//compoment已經(jīng)掛載的函數(shù)
//界面渲染完之后莫换,在進(jìn)行一些數(shù)據(jù)處理霞玄,比如網(wǎng)絡(luò)數(shù)據(jù)加載,比如本地?cái)?shù)據(jù)加載
componentDidMount() {
console.log("AndroidTestComponent=====componentDidMount")
}
//作為子控件時(shí)拉岁,當(dāng)期屬性被改變時(shí)調(diào)用
componentWillReceiveProps(nextProps) {
console.log("AndroidTestComponent=====componentWillReceiveProps")
}
//component將要更新時(shí)調(diào)用
componentWillUpdate(nextProps, nextState) {
console.log("AndroidTestComponent=====componentWillUpdate")
}
//component更新后調(diào)用
componentDidUpdate(prevProps, prevState) {
console.log("AndroidTestComponent=====componentDidUpdate")
}
//component銷毀時(shí)調(diào)用
componentWillUnmount() {
console.log("AndroidTestComponent=====componentWillUnmount")
BackAndroid.removeEventListener('hardwareBackPress',this.onBackAndroid)
}
}
conststyles=StyleSheet.create({
container: {
flex:1,
justifyContent:'flex-start',
alignItems:'stretch',
backgroundColor:'white'
},
lineStyle: {
backgroundColor:'grey',
height:0.3,
},
loadText: {
fontSize:20,
textAlign:'center',
margin:10
},
loadView: {
flex:1,
alignItems:'center',
justifyContent:'center'
},
});
//另一種定義props的方法坷剧,如果static defaultProps也定義了,這個(gè)會(huì)覆蓋上面的
// AndroidTestComponent.defaultProps = {
//? ? name:'xiaoerlang'
// }
//進(jìn)行注冊(cè) 'RNProject'為項(xiàng)目名稱 AndroidTestComponent 為啟動(dòng)的component
AppRegistry.registerComponent('RNProject', () => AndroidTestComponent);
過程中遇到的問題及解決方案:
react native - expected acomponent class, got [object Object]
該錯(cuò)誤是引用了小寫的組件喊暖,組件首字母一定要大寫惫企,比如<splash/>應(yīng)該寫成<Splash>