嵌套導航的意思就是說你新建了一個導航器,在這個導航器的導航頁中又包含了另一個導航器。比如:
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布局 而不是來出來你的代碼邏輯,如果你先給你的公司穿件一系列的頁面,你可以將其歸類為幾個對象或者數組,而不是用導航器來分類