在 Expo Router 中的导航布局

编辑

学习如何通过使用目录和布局文件构建不同页面之间的关系。


Expo Router 布局文件简介
Expo Router 布局文件简介

什么是布局文件,如何在屏幕之间导航,以及如何使用重定向阻止访问。

每个 app 目录中的目录(包括 app 本身)可以在该目录内定义一个 _layout.tsx 文件作为布局。该文件定义了该目录内所有页面的排列方式。在这里,您可以定义堆栈导航器、选项卡导航器、抽屉导航器或您希望用于该目录中的页面的任何其他布局。布局文件导出一个默认组件,该组件在您导航到该目录内的任何页面之前呈现。

让我们看几个常见的布局场景。

根布局

几乎每个应用都将在 app 目录中直接包含一个 _layout.tsx 文件。这是根布局,代表您导航的入口点。除了描述您应用的顶级导航器外,该文件是放置初始化代码的地方,这些代码可能之前放在 App.jsx 文件中,例如加载字体、与启动画面交互或添加上下文提供者。

以下是根布局的示例:

app/_layout.tsx
import { useFonts } from 'expo-font'; import { Stack } from 'expo-router'; import * as SplashScreen from 'expo-splash-screen'; import { useEffect } from 'react'; SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), }); useEffect(() => { if (loaded) { SplashScreen.hide(); } }, [loaded]); if (!loaded) { return null; } return <Stack />; }

上述示例初始显示启动画面,然后在字体加载后呈现堆栈导航器,这将导致您的应用继续至初始路由。

堆栈

您可以在根布局中实现堆栈导航器,如上所示,或在目录中的任何其他布局文件中。假设您有一个文件结构,在目录中有一个堆栈:

app
products
  _layout.tsx
  index.tsx
  [productId].tsx
  accessories
   index.tsx

如果您希望 app/products 目录中的所有内容以堆栈关系排列,请在 _layout.tsx 文件中返回一个 Stack 组件:

app/products/_layout.tsx
import { Stack } from 'expo-router'; export default function StackLayout() { return <Stack />; }

当您导航到 /products 时,它将首先转到默认路由,即 products/index.tsx。如果您导航到 /products/123,那么该页面将被推送到堆栈中。默认情况下,堆栈将在头部呈现一个返回按钮,该按钮将当前页面从堆栈中弹出,使用户返回到上一页。即使页面不可见,如果它仍然被推入堆栈,它仍然在呈现中。

Stack 组件实现了 React Navigation 的原生堆栈,并且可以使用相同的屏幕选项。不过,您无需在导航器内具体定义页面。目录中的文件将自动被视为堆栈中的合格路由。不过,如果您想定义屏幕选项,可以在 Stack 组件内添加一个 Stack.Screen 组件。name 属性应与路由名称匹配,但您无需提供 component 属性;Expo Router 会自动映射:

app/products/_layout.tsx
import { Stack } from 'expo-router'; export default function StackLayout() { return ( <Stack> <Stack.Screen name="[productId]" options={{ headerShown: false }} /> </Stack> ); }

虽然可以嵌套导航器,但请确保仅在确实需要时才这样做。在上述示例中,如果您想将 products/accessories/index.tsx 推入堆栈,则不必在 accessories 目录中拥有额外的 _layout.tsx 和一个 Stack 导航器。那将定义一个嵌套在第一个堆栈内的另一个堆栈。添加仅影响 URL 的目录是可以的,否则,请使用与父目录相同的导航器。

选项卡

类似于堆栈,您可以在布局文件中实现选项卡导航器,目录中的所有路由将被视为选项卡。考虑以下文件结构:

app
(tabs)
  _layout.tsx
  index.tsx
  feed.tsx
  profile.tsx

_layout.tsx 文件中,返回一个 Tabs 组件:

app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; export default function TabLayout() { return ( <Tabs> <Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ({ color }) => <MaterialIcons size={28} name="house.fill" color={color} />, }} /> <!-- 在这里添加更多标签 --> </Tabs> ); }

这将使 index.tsxfeed.tsxprofile.tsx 文件在同一底部选项卡导航器中一起出现。该 Tabs 组件使用 React Navigation 的原生底部选项卡 并支持相同的选项。

Tabs 中,您可能希望在导航器中定义选项卡,因为这会影响选项卡的出现顺序、标题和选项卡内的图标。index 路由将是默认选中的选项卡。

插槽

在某些情况下,您可能希望没有导航器的布局。这对于在当前路由周围添加页眉或页脚,或在目录内部显示模态是很有帮助的。在这种情况下,您可以使用 Slot 组件,它作为当前子路由的占位符。

考虑以下文件结构:

app
social
  _layout.tsx
  index.tsx
  feed.tsx
  profile.tsx

例如,您可能希望用页眉和页脚包装 social 目录中的任何路由,但您希望在页面之间的导航仅替换当前页面,而不是将新页面推入堆栈,这样可以后续用“后退”导航操作将其弹出。在 _layout.tsx 文件中,返回一个被页眉和页脚包围的 Slot 组件:

app/social/_layout.tsx
import { Slot } from 'expo-router'; export default function Layout() { return ( <> <Header /> <Slot /> <Footer /> </> ); }

其他布局

这只是一些常见布局的示例,让您了解它是如何工作的。您可以在布局中做更多事情: