嵌套导航的意思就是说你新建了一个导航器,在这个导航器的导航页中又包含了另一个导航器。比如:
function Home() { return ( <Tab.Navigator> <Tab.Screen name="Feed" component={Feed} /> <Tab.Screen name="Messages" component={Messages} /> </Tab.Navigator> ); } function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="Profile" component={Profile} /> <Stack.Screen name="Settings" component={Settings} /> </Stack.Navigator> </NavigationContainer> ); }
在上述的例子中,Home组件包含了一个tab导航器。同时,这个Home组件也是堆栈导航器中的一个导航页。这样看来这个tab导航器就是嵌套在了堆栈导航器中。
导航器的嵌套其实跟我们通常使用的组件的嵌套类似,一般情况下,我们通常会嵌套多个导航器
如何嵌套导航器
当我们将导航器嵌套起来的时候,有几点需要特别注意:
每个导航器保管它自己的导航历史
比如,当你在一个被嵌套的堆栈导航器上点击返回按钮的时候,它会返回到本导航器(就是被嵌套的堆栈导航器)导航历史中的上一页,而不是返回到上级导航器中。
导航的action会优先由当前导航器处理如果当前导航器不能处理则通过冒泡的方式由上一级导航器处理
比如,你在一个被嵌套的页面中调用navigation.goBack(),那么只有当你在该导航器的首页的时候你才会返回到父导航器中。其他的action比如navigate工作原理相同。也就是说,只有当嵌套的导航器不能处理这个action的时候,父导航器才会视图去处理它。在上面的例子中,当你在Profile页面中调用navigate('Settings')的时候,被嵌套的堆栈导航器将会处理它,但是如果你调用navigate('Home'),那么tab导航器会处理它。
导航器的一些特定方法在子导航器中同样可用
比如,在drawer导航器中嵌套了一个stack 导航器,那么drewer导航器的openDrawer、closeDrawer方法在被嵌套的stack导航器的navigation属性中依然是可用的。但是如果stack导航器没有嵌套在drawer导航器中,那么这些方法是不可访问的。
同样,如果你在stack导航器中嵌套了一个tab导航器,那么tab导航器页面中的navigation属性会新得到push和replace这两个方法。
被嵌套的导航器不会响应父级导航器的事件
比如,如果你stack导航器被嵌套在了tab导航器中,那么stack导航器的页面不会响应由父tab导航器触发的事件,比如我们使用navigation.addListener绑定的tabPress事件。为了能够响应父级导航器的事件,你可以使用navigation.dangerouslyGetParent().addListener来监听父级导航器的事件。
父级导航器的UI先于子导航器被渲染
比如,当你在drawer导航器中嵌套了一个stack导航器,你会看到抽屉的效果先被渲染出来,接着才是渲染stack导航器的头部,但是如果你将drawer导航器嵌套在了stack导航器中,那么则会先渲染stack导航器的头部再渲染抽屉效果。这是一个很关键的知识点。
在你的app开发中,你可以根据你的需求来选用下面这些模式:
- 在drawer导航器的每个页面嵌套stack导航器----即先渲染抽屉效果再渲染stack导航器的头部
- stack导航器的首页嵌套tab导航器----当你通过push跳转页面的时候,新的页面会覆盖掉标签栏。
- tab导航器的每个页面都嵌套stack导航器----tab导航器的标签栏仍然可见。常见的就是点击tab将stack置顶。
在嵌套的导航器中跳转页面
我们来看一下下面这段代码
function Root() { return ( <Stack.Navigator> <Stack.Screen name="Profile" component={Profile} /> <Stack.Screen name="Settings" component={Settings} /> </Stack.Navigator> ); } function App() { return ( <NavigationContainer> <Drawer.Navigator> <Drawer.Screen name="Home" component={Home} /> <Drawer.Screen name="Root" component={Root} /> </Drawer.Navigator> </NavigationContainer> ); }
现在你想从你的Home页面中跳转到Root页面
navigation.navigate('Root');
这是有效的,Root组件的首页Profile会显示出来,但是,有时候你可能希望显示自己指定的页面。为了实现这个,你可以在参数中携带页面的名字。
navigation.navigate('Root',{screen:'Settings'});
现在,Setting 页面就会被显示出来,当然你也可以用下面的方式传递参数:
navigation.navigate('Root', { screen: 'Settings', params: { user: 'jane' }, });
如果导航器液晶被渲染出来了,跳转到另一个页面会push一个新的页面到stack导航器中。
你也可以使用类似的方法跳转到深度嵌套的页面中。需要注意的是第二个参数(也就是你要跳转的页面)只是一个参数,就像下面所示:
navigation.navigate('Root', { screen: 'Settings', params: { screen: 'Sound', params: { screen: 'Media', }, }, });
在上面的例子中,你会跳转到Media页面,这个页面位于Setting页面所嵌套的导航器的Sound页面所嵌套的导航器中。
这种导航器的嵌套方法看起来与先前版本的似乎有很大不同,先前版本所有的配置是静态的,所以,React Navigation只能通过递归方式静态的查找所有导航器的列表以及他们的页面。但是现在有了动态配置,React Navigation并不知道那个页面是可用的,以及它在哪里直到导航器包含这个页面。通常,页面不会渲染它的内容除非你跳转到该页面上,所以尚未渲染的导航器的配置文件是不可用的。所以你必须指定你要跳转的页面的层级结构。这也是你为什么应该尽量少嵌套导航器,这样可以使你的代码更为简洁。
嵌套操作的最佳实践
我们建议尽量少嵌套导航器。因为嵌套导航器有很多不利的方面
代码变得更为繁琐
其会造成一个很深的嵌套层级结构,这可能会造成内存和性能问题
同种类型的导航器相互嵌套会导致逻辑混乱
推荐用嵌套导航器的方法来实现你的UI布局 而不是来出来你的代码逻辑,如果你先给你的公司穿件一系列的页面,你可以将其归类为几个对象或者数组,而不是用导航器来分类