Expo Router 中的常见导航模式

编辑

将 Expo Router 的基础知识应用于您可以在应用程序中使用的现实导航模式。


现在您已经知道了在 Expo Router 中如何命名和排列文件与目录的基础知识,让我们应用这些知识,看看您在应用程序中可能使用的一些现实导航模式。

标签页中的堆栈:嵌套导航器

如果您的应用程序的典型起始点是一组标签,但一个或多个标签可能与多个屏幕相关,则在标签中嵌套堆栈导航器通常是最佳选择。此模式通常会产生直观的 URL,并且在桌面 Web 应用程序上的可扩展性良好,因为主标签通常始终可见。

请考虑以下导航树:

app
(tabs)
  _layout.tsx
  index.tsx单页标签
  feed
   _layout.tsx带有堆栈的标签
   index.tsx
   [postId].tsx
  settings.tsx单页标签

app/(tabs)/_layout.tsx 文件中,返回一个 Tabs 组件:

app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router'; export default function TabLayout() { return ( <Tabs screenOptions={{ headerShown: false }}> <Tabs.Screen name="index" options={{ title: 'Home' }} /> <Tabs.Screen name="feed" options={{ title: 'Feed' }} /> <Tabs.Screen name="settings" options={{ title: 'Settings' }} /> </Tabs> ); }

app/(tabs)/feed/_layout.tsx 文件中,返回一个 Stack 组件:

app/(tabs)/feed/_layout.tsx
import { Stack } from 'expo-router'; export const unstable_settings = { initialRouteName: 'index', }; export default function FeedLayout() { return <Stack />; }

现在,在 app/(tabs)/feed 目录中,您可以有指向不同帖子的 Link 组件(例如,/feed/123)。这些链接将把 feed/[postId] 路由推入堆栈,同时保持标签导航器可见。

您还可以使用相同的 URL 从任何其他标签导航到动态标签中的帖子。使用 withAnchor 配合 initialRouteName,确保 feed/index 路由始终是堆栈中的第一个屏幕:

app/(tabs)/feed/index.tsx
<Link href="/feed/123" withAnchor> Go to post </Link>

您还可以在外部堆栈导航器中嵌套标签。这通常更有助于在标签上方显示模态。

嵌套导航器

了解如何在您的 Expo Router 应用中使用嵌套导航器的更多信息。

一个屏幕,两个标签:共享路由

路由组可用于在两个不同的标签之间共享单个屏幕。考虑一下具有动态标签和搜索标签的导航树,它们都共享查看用户资料的页面:

app
(tabs)
  _layout.tsx
  (feed)
   index.tsx默认路由
  (search)
   search.tsx
  (feed,search)
   _layout.tsx两个标签共享的布局
   users
    [username].tsx共享用户资料页面

每个标签都被放入一个组中,因此您可以定义一个第三个目录以在两个组之间共享路由 (app/(tabs)/(feed,search)/)。即使有额外的层,app/(tabs)/(feed)/index.tsx 仍然是最近的索引,因此它将是默认路由。

app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router'; export default function TabLayout() { return ( <Tabs> <Tabs.Screen name="(feed)" options={{ title: 'Feed' }} /> <Tabs.Screen name="(search)" options={{ title: 'Search' }} /> </Tabs> ); }

(feed)(search) 路由组都包含堆栈,因此它们也可以共享单个布局:

app/(tabs)/(feed,search)/_layout.tsx
import { Stack } from 'expo-router'; export default function SharedLayout() { return <Stack />; }

共享组还可以只包含共享页面,每个不同的组都有自己的布局文件。

现在,两个标签都可以导航到 /users/evanbacon 并查看相同的用户资料页面。

当您已经专注于一个标签并导航到一个用户时,您将留在当前标签的组中。但是,当直接从应用程序外部深度链接到用户资料页面时,Expo Router 必须选择两个组中的一个,因此它将选择按字母顺序排列的第一个组。因此,深度链接到 /users/evanbacon 将会在动态标签中显示用户资料。

共享路由

了解不同路由如何在 Expo Router 中共享相同的 URL。

仅限经过身份验证的用户:受保护路由

对于需要身份验证的移动应用程序,您可能会有一组路由仅应对经过身份验证的用户进行访问。

例如,请考虑以下导航树,其中您有一个底部标签布局、一个登录页面、一个创建账户页面,以及一个仅应对经过身份验证的用户可见的模态:

app
_layout.tsx根布局
(tabs)
  _layout.tsx
  index.tsx受保护的
  settings.tsx受保护的
sign-in.tsx
create-account.tsx
modal.tsx受保护的

当您的应用程序首次启动时,路由器将试图打开根索引, app/(tabs)/index.tsx。如果您将此屏幕包装在一个 Stack.Protected 中,并将 guard={false},则该屏幕将变得不可访问,并且将打开下一个可用屏幕。在这个例子中,将打开 sign-in 屏幕,因为它是下一个可用路由。

app/_layout.tsx
import { Stack } from 'expo-router'; import { useAuthState } from '@/utils/authState'; export default function RootLayout() { const { isLoggedIn } = useAuthState(); return ( <Stack> <Stack.Protected guard={isLoggedIn}> <Stack.Screen name="(tabs)" /> <Stack.Screen name="modal" /> </Stack.Protected> <Stack.Protected guard={!isLoggedIn}> <Stack.Screen name="sign-in" /> <Stack.Screen name="create-account" /> </Stack.Protected> </Stack> ); }

通过这种方式,您可以从商店中提取您的身份验证状态,并显示适当的屏幕。如果身份验证状态发生变化,布局将重新渲染,因此如果 isLoggedInfalse 更改为 true,应用将自动导航到 (tabs) 组的根。

受保护路由的另一个好处是,它们会在您直接深度链接到页面时进行检查。例如,如果未经过身份验证的用户深度链接到上面的模态屏幕,他们将被重定向到登录页面。

受保护的路由还可以用于有条件地显示底部标签。在这个例子中,vip 标签仅对身份验证用户显示:

app/(tabs)/_layout.tsx
import { Stack } from 'expo-router'; import { useAuthState } from '@/utils/authState'; export default function TabsLayout() { const { isVip } = useAuthState(); return ( <Tabs> <Tabs.Screen name="index" /> <Tabs.Protected guard={isVip}> <Tabs.Screen name="vip" /> </Tabs.Protected> <Tabs.Screen name="settings" /> </Tabs> ); }
Expo Router 身份验证

查看实现使用受保护路由的身份验证的深入指南。

有时候最好的路由根本不是路由

将您的导航状态分离到不同的路由中是为了服务于您和您的应用程序。有时候,最佳模式根本不涉及导航到另一个路由。由于布局文件只是 React 组件,您可以使用它们来显示各种 UI,除了、旁边或代替导航器。

回想一下身份验证,如果用户在未登录的情况下根本不应该访问某些页面,受保护路由的设置效果很好。但是当未经过身份验证的用户可以以只读模式浏览应用程序时,该怎么办?在这种情况下,您可能希望在应用中显示一个登录模态,而不是将用户重定向到登录页面:

app/(logged-in)/_layout.tsx
import { Modal } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Stack } from 'expo-router'; export default function Layout() { const isAuthenticated = /* 检查有效的身份验证令牌 / 会话 */ return ( <SafeAreaView> <Stack /> <Modal visible={!isAuthenticated}>{/* 登录 UX */}</Modal> </SafeAreaView> ); }
Expo Router 中的模态

了解在 Expo Router 中显示模态的多种模式,包括在布局文件中使用模态。