Navigator已經被React Native廢棄了。也許你可以在另外的一個依賴庫里react-native-deprecated-custom-components里找到。不過既然官方推薦的是react-navigation那我們就來看看這個東西到底有什么好的,值不值得用。
一句話概括的話,react-navigation非常值得用。之前配置一個Navigator非常的繁瑣,但是使用react-navigation的任何一個導航組件都非常簡單。項目的github地址在這里。
react-navigation包括下面三個Navigator:
StackNavigator: 這個組件是用來代替之前的Navigator的。凡是維持一種“先進后廚”的棧式導航的話就可以用這個。TabNavigator:這個組件和iOS的`TabBarController。看起來是這樣的。DrawerNavigator:這個組件就是抽屜式的導航菜單。在React Native里只有Android才有:DrawerLayoutAndroid,在iOS里是沒有的。有了DrawerNavigator,兩個平台都可以用了。
我會在下文里主要介紹StackNavigator和DrawerNavigator。對於TabNavigatgor它的使用非常簡單,當你回了前面的兩種的時候你就自然可以搞定它了。
Stack Navigator
在
react-native init AwesomeProject
命令后生成的默認項目里,查看index.js文件:
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('AwesomeProject', () => App);
APP開始執行后運行的就是App組件。也就是App.js文件export的是什么組件,App就運行什么組件。
在App.js文件中,去掉export default。就如我們的demo做的一樣,添加一個MessageContainer.js文件,並添加demo中的內容。這樣在其中我們已經有了App、MessageContainer兩個組件。
下面看下如何配置。
最簡單的:
export default NavHome = StackNavigator({
Home: {
screen: NavApp,
},
Message: {
screen: MessageContainer,
},
})
導出StackNavigator方法生成的組件NavHome。運行起來之后,理論上就可以導航了。但是會有問題,因為這時還沒有能夠跳轉的觸發點。所以,我們還要做如下的修改。
修改App.js文件的內容。在其中添加一個按鈕,點擊之后可以進入到MessageContainer組件。修改MessageContainer.js文件,在里面添加一個按鈕返回。
//App.js
<Button onPress={this.props.navigation.navigate('Message')} title={'To message'} />
//MessageContainer.js
<Button onPress={this.props.navigation.goBack()} title={'Go Back'} />
但是,這樣還是demo的水平,離真正的產品級使用還差很多。一般的App,在push到下一個頁面的時候會點擊navigation bar的回退按鈕返回上一頁。我們就來實現這個功能。
使我們首先看一下StackNavigator的API:
StackNavigator(RouteConfigs, StackNavigatorConfig)
通過查看文檔,要實現這個功能需要在RouteConfigs里面增加navigationOptions來達到。如下:
export default NavHome = StackNavigator({
Home: {
screen: App,
navigationOptions: ({navigation}) => ({
title: 'Home',
headerLeft: (<Button onPress={() => navigation.navigate('DrawerToggle')} title={'User'} />),
headerRight: (<Button onPress={() => navigation.navigate('Message')} title={'Message'} />),
})
},
Message: {
screen: MessageContainer,
navigationOptions: ({navigation}) => ({
title: "Message",
headerLeft: (<Button title='Back' onPress={() => {navigation.goBack();}} />)
})
},
});
詳細看一下navigationOptions。
- title:是導航欄上顯示的title。
- headerLeft: 是導航欄左側的組件。我這里放了一個按鈕。更好的是放置一個
TouchableOpacity組件。因為按鈕在iOS上還好,但是在Android上就是一個明晃晃的按鈕啊,各種邊框和陰影。 - headerRight: 是導航欄右側的組件。
在MessageContainer的導航欄上就只需要一個“返回”按鈕,所以只有一個headerLeft就足夠了。在首頁上的導航欄的headerLeft是用來觸發稍后講到的DrawerNavigator的。
這樣,這個靠譜的導航就完成了。
Drawer Navigator
DrawerNavigator和StackNavigator的配置很類似。
const NavApp = DrawerNavigator({
Home: {
screen: App,
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
});
這個時候看起來是這樣的:

但是我想要的效果是這樣的:

顯然,文檔里提供的一些簡單的定制是不能完成這樣的效果的。於是,我們查看文檔,發現有辦法直接替換掉默認的Drawer實現,非常簡單:
const NavApp = DrawerNavigator({
Home: {
screen: App,
},
User: {
screen: UserContainer,
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
}, {
contentComponent: props => (<UserDrawer items={props} />)
})
看下API:
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
只要叫上DrawerNavigatorConfig配置里的contentComponent配置。也就是上面配置的第二個參數。
{
contentComponent: props => (<UserDrawer items={props} />)
}
contentComponent就是drawer的內容組件。這里我們用的是UserDrawer組件,並把props傳遞了進去。
這樣我們想要的抽屜式菜單就實現了。
StackNavigator和DrawerNavigator結合使用
現在把這兩個組件結合在一起使用。在首頁上的導航欄里的兩個按鈕,左側的開啟drawer導航,右側的是“message”按鈕,使用StackNavigator組件導航。
如果是在drawer導航里使用StackNavigator的話,那么只要這樣配置:
export default NavHome = StackNavigator({
Home: {
screen: NavApp,
navigationOptions: ({navigation}) => ({
title: 'Home',
headerLeft: (<Button onPress={() => navigation.navigate('DrawerToggle')} title={'User'} />),
headerRight: (<Button onPress={() => navigation.navigate('Message')} title={'Message'} />),
})
},
Message: {
screen: MessageContainer,
navigationOptions: ({navigation}) => ({
title: "Message",
headerLeft: (<Button title='Back' onPress={() => {navigation.goBack();}} />)
})
},
});
export default NavApp = DrawerNavigator({
Home: {
screen: NavHome, // ***
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
}, {
contentComponent: props => (<UserDrawer items={props} />)
})
要在Drawer導航里用stack導航,那么就在drawer導航里的某個路由選項里加上screen: NavHome。這個NavHome就是stack導航。反之,則是在stack導航里的某個route選項的screen上指定drawer導航。
但是在使用上還是有一點區別的。如果drawer導航里包含stack導航。那么drawer導航菜單的最高點是在屏幕的最高點。反之,如果drawer導航被stack導航包含的話,drawer導航菜單的最高點是在導航欄的下方的。如圖:


回到正題。從drawer導航菜單跳轉到任何的頁面后如何跳轉回來呢?還是老方法:
export default class MyWalletView extends React.Component {
render() {
return (
<TouchableOpacity
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
onPress={() => this.props.navigation.goBack()}>
<Text>{'My Wallet'}</Text>
</TouchableOpacity>
);
}
}
調用props傳入的navigation的方法來實現返回:
this.props.navigation.goBack()
總結
更多請看代碼吧。留下來TabNavigator來給各位讀者朋友實踐一下練練手吧。其實配置的簡單程度比早前React Native里的Navigator已經降低了很多了。
StackNavigator里還有除了navigate()和goBack()兩個方法之外,還有其他的一些方法可以調用。實際的App交互中也並不是只有導航到某一頁,然后再從那一頁跳轉回來這么簡單。后面有機會會講到這方面的內容。
