前言
默认你已经对(Stac/Drawer/Tab)三种导航已经有了基本的了解(V6版本),另外我下面提供的demo都是可直接copy运行的,要是运行不了,那就自行debug下吧
如果期望对react-navigation快速的有个系统的了解,点击查看react-navigation v6 中文极速版
一、Tab嵌套Stack
-
代码示例
import * as React from 'react' import {Button, Text, View, StyleSheet} from 'react-native' import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {NavigationContainer} from '@react-navigation/native' const STYLE = StyleSheet.create({ center: { flex: 1, justifyContent: 'center', alignItems: 'center' } }) function Screen3() { return ( <View style={STYLE.center}> <Text>Screen 3</Text> </View> ) } function Screen1({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 1</Text> <Button title="一层一层进入 Screen3" onPress={() => navigation.navigate('BStack', {screen: 'Screen3'})} /> <Button title="直接进入Go to Screen3" onPress={() => navigation.navigate('Screen3')} /> </View> ) } function Screen2({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 2</Text> <Button title="直接进入 Screen3" onPress={() => navigation.navigate('Screen3')} /> </View> ) } const HomeStack = createNativeStackNavigator() function AStackScreen() { return ( <HomeStack.Navigator> <HomeStack.Screen name="Screen1" component={Screen1} /> <HomeStack.Screen name="Screen3" component={Screen3} /> </HomeStack.Navigator> ) } const SettingsStack = createNativeStackNavigator() function BStackScreen() { return ( <SettingsStack.Navigator> <SettingsStack.Screen name="Screen2" component={Screen2} /> <SettingsStack.Screen name="Screen3" component={Screen3} /> </SettingsStack.Navigator> ) } const Tab = createBottomTabNavigator() export default function Demo01Navigator() { return ( <NavigationContainer> <Tab.Navigator screenOptions={{headerShown: false}}> <Tab.Screen name="AStack" component={AStackScreen} /> <Tab.Screen name="BStack" component={BStackScreen} /> </Tab.Navigator> </NavigationContainer> ) }
-
结构
Tab AStack Screen1 Screen3 BStack Screen2 Screen3
-
一些特性
- 因为最外层是Tab,因此所有跳转的Screen底部都会有TabBar
- 跳转同栈下的Screen,可以直接跳转,比如Screen1跳转到AStack中的Screen3的方式如下:navigation.navigate(‘Screen3’)
- 跳转不同栈的Screen,禁止直接跳转,需要指定层级,比如Screen1跳转到BStack中的Screen3的方式如下:navigation.navigate(‘BStack’, {screen: ‘Screen3’}),并且该case下也直接触发了Tab的切换
二、Stack嵌套Tab
-
示例代码
import * as React from 'react' import {Button, Text, View, StyleSheet} from 'react-native' import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {NavigationContainer} from '@react-navigation/native' const STYLE = StyleSheet.create({ center: { flex: 1, justifyContent: 'center', alignItems: 'center' } }) function Screen1({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 1</Text> <Button title="进入Tab下的Screen2" onPress={() => navigation.jumpTo('Screen2')} /> <Button title="进入Stack下的Screen2" onPress={() => navigation.push('Screen2')} /> </View> ) } function Screen2({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 2</Text> <Button title="直接进入Stack下的Screen3" onPress={() => navigation.navigate('Screen3')} /> </View> ) } function Screen3({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 3</Text> <Button title="进入Tab下的Screen1" onPress={() => navigation.navigate('Screen1')} /> </View> ) } const Tab = createBottomTabNavigator() function TabNavigator() { return ( <Tab.Navigator screenOptions={{headerShown: false}}> <Tab.Screen name="Screen1" component={Screen1} /> <Tab.Screen name="Screen2" component={Screen2} /> </Tab.Navigator> ) } const Stack = createNativeStackNavigator() export default function Demo02Navigator() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Tab" component={TabNavigator} /> <Stack.Screen name="Screen2" component={Screen2} /> <Stack.Screen name="Screen3" component={Screen3} /> </Stack.Navigator> </NavigationContainer> ) }
-
结构
Stack Tab Screen1 Screen2 Screen2 Screen3
-
一些特性
- 只有Tab下的Screen才有tabBar,若某个Screen即在Tab下也在Stack下,就看跳转方式了,比如在Screen1中
- 使用jumpTo跳转到Screen2,去到的就是Tab下的Screen2(有tabBar),
- 使用push跳转到Screen2,去到的就是Stack下的Screen2(无tabBar),
- 使用navigate跳转到Screen2去到的也是Tab下的Screen2(有tabBar),初步分析是因为Tab初始化时路由栈中有了Screen2,在结合navigate的特性,去到Tab下的Screen2也解释得通
- 这种case下除了固定的tab外,其他的跳转就不需要考虑嵌套层级,直接指定名字即可
- 针对指定页面不显示tabBar,官方也推荐这种嵌套结构,点击查看官方说明
- 只有Tab下的Screen才有tabBar,若某个Screen即在Tab下也在Stack下,就看跳转方式了,比如在Screen1中
三、Drawer嵌套
-
示例代码
import * as React from 'react' import {Button, Text, View, StyleSheet} from 'react-native' import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {createDrawerNavigator} from '@react-navigation/drawer' import {NavigationContainer} from '@react-navigation/native' const STYLE = StyleSheet.create({ center: { flex: 1, justifyContent: 'center', alignItems: 'center' } }) function Screen1({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 1</Text> <Button title="进入Drawer下的Screen3" onPress={() => navigation.navigate('LeftDrawer', {screen: 'Screen3'})} /> </View> ) } function Screen2({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 2</Text> </View> ) } function Screen3({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 3</Text> <Button title="打开抽屉" onPress={() => navigation.openDrawer()} /> <Button title="进入Stack下的Screen4" onPress={() => navigation.navigate('Screen4')} /> </View> ) } function Screen4({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 3</Text> <Button title="进入Tab下的Screen1" onPress={() => navigation.navigate('Screen1')} /> </View> ) } const Tab = createBottomTabNavigator() function TabNavigator() { return ( <Tab.Navigator screenOptions={{headerShown: false}}> <Tab.Screen name="Screen1" component={Screen1} /> <Tab.Screen name="Screen2" component={Screen2} /> </Tab.Navigator> ) } const Drawer = createDrawerNavigator() function LeftDrawerNavigator() { return ( <Drawer.Navigator drawerContent={() => { return <Text>XXXXXx</Text> }} screenOptions={{swipeEnabled: false, "headerShown": true, "drawerPosition": 'left'}} > <Drawer.Screen name="Screen3" options={{title: 'Screen3'}} component={Screen3} /> </Drawer.Navigator> ) } const Stack = createNativeStackNavigator() export default function Demo03Navigator() { return ( <NavigationContainer> <Stack.Navigator initialRouteName="Tab"> <Stack.Screen name="Tab" component={TabNavigator} /> <Stack.Screen name="LeftDrawer" options={{"headerShown": false}} component={LeftDrawerNavigator} /> <Stack.Screen name="Screen4" component={Screen4} /> </Stack.Navigator> </NavigationContainer> ) }
-
结构
Stack Tab Screen1 Screen2 LeftDrawer Screen3 Screen4
-
一些特性
- 只有Drawer下的Screen才有抽屉效果,若想有右边抽屉在LeftDrawer的同级增加一个RightDrawer即可,那么如何让一个Screen既有左边抽屉又有右边抽屉,有两种方案:
- 将Screen即放在LeftDrawer下,也放在RightDrawer下,跳转前指定栈层级即可
这种方式让一个Screen同时只有一个导航效果,要么左要么右,具体是左还是右,取决于父级是谁
- Drawer的父子嵌套,详情见官方说明
这种方式可以让一个Screen,同时拥有左右两种导航效果
- 将Screen即放在LeftDrawer下,也放在RightDrawer下,跳转前指定栈层级即可
- Drawner默认也带有导航栏,可通过options设置具体使用谁的导航栏。另外因为Drawer是嵌套在stack下,因此抽屉效果始终会在stack的导航栏下层显示
- 跳转有Drawer效果的页面时,需要确定他的父级,个人对这种模式很不爽,我理想的状态是在打开抽屉效果时指定左边还是右边,这种比较符合开发习惯,详情见第四种嵌套
- 只有Drawer下的Screen才有抽屉效果,若想有右边抽屉在LeftDrawer的同级增加一个RightDrawer即可,那么如何让一个Screen既有左边抽屉又有右边抽屉,有两种方案:
四、Drawer作为根导航嵌套
-
代码示例
import * as React from 'react' import {Button, Text, View, StyleSheet} from 'react-native' import {createNativeStackNavigator} from '@react-navigation/native-stack' import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' import {NavigationContainer} from '@react-navigation/native' import { createDrawerNavigator } from '@react-navigation/drawer' const STYLE = StyleSheet.create({ center: { flex: 1, justifyContent: 'center', alignItems: 'center' } }) function Screen1({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 1</Text> <Button title="进入Scrrn3" onPress={() => navigation.navigate('Screen3')} /> </View> ) } function Screen2({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 2</Text> </View> ) } function Screen3({navigation}: any) { return ( <View style={STYLE.center}> <Text>Screen 3</Text> <Button title="打开左抽屉" onPress={() => navigation.getParent('LeftDrawer').openDrawer()} /> <Button title="打开右抽屉" onPress={() => navigation.getParent('RightDrawer').openDrawer()} /> </View> ) } const Tab = createBottomTabNavigator() function TabNavigator() { return ( <Tab.Navigator screenOptions={{headerShown: false}}> <Tab.Screen name="Screen1" component={Screen1} /> <Tab.Screen name="Screen2" component={Screen2} /> </Tab.Navigator> ) } const LeftDrawer = createDrawerNavigator(); const RightDrawer = createDrawerNavigator(); const RightDrawerNavigator = () => { return ( <RightDrawer.Navigator id="RightDrawer" screenOptions={{ drawerPosition: 'right', headerShown: false }} > <RightDrawer.Screen name="stack" component={StackNavigator} /> </RightDrawer.Navigator> ); }; const Stack = createNativeStackNavigator() function StackNavigator() { return ( <Stack.Navigator> <Stack.Screen name="Tab" component={TabNavigator} /> <Stack.Screen name="Screen3" component={Screen3} /> </Stack.Navigator> ) } export default function Demo04Navigator() { return ( <NavigationContainer> <LeftDrawer.Navigator id="LeftDrawer" screenOptions={{ drawerPosition: 'left', headerShown: false }}> <LeftDrawer.Screen name='LeftDrawer' component={RightDrawerNavigator}/> </LeftDrawer.Navigator> </NavigationContainer> ) }
-
结构
LeftDrawer RightDrawer Stack Tab Screen1 Screen2 Scrren3 Scrren4
-
一些特性
- 该嵌套模式,可以实现所有的Screen都同时支持左右两种抽屉效果,并且打开哪个抽屉是在调用打开抽屉API时决定的,而非在进入当前页面时决定
- 因为抽屉是根路由,因此抽屉效果是覆盖在stack的navBar上
- 这种嵌套在web上加载首页时会快速的显示左右抽屉并消失,目前没有找到合适的解决方案
五、总结
没有最好,只有最适合自己业务的,大家可根据业务情况做抉择,或者扩展出更适合自己业务的导航嵌套模式