受保护的路由

编辑页面

了解如何使屏幕无法通过客户端导航访问。


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

观看:使用受保护路由
观看:使用受保护路由

在 Expo Router 中使用受保护路由,根据身份验证状态限制屏幕访问。

概览

受保护屏幕允许你通过客户端导航防止用户访问某些路由。如果用户尝试导航到受保护屏幕,或者某个屏幕在处于活动状态时变为受保护状态,他们将被重定向到锚点路由(通常是 index 屏幕)或堆栈中的第一个可用屏幕。

src
app
  _layout.tsx
  index.tsx
  about.tsx
  login.tsx仅在未认证时可用
  private
   _layout.tsx仅在已认证时可用
   index.tsx
   page.tsx
src/app/_layout.tsx
import { Stack } from 'expo-router'; const isLoggedIn = false; export function AppLayout() { return ( <Stack> <Stack.Protected guard={!isLoggedIn}> <Stack.Screen name="login" /> </Stack.Protected> <Stack.Protected guard={isLoggedIn}> <Stack.Screen name="private" /> </Stack.Protected> {/* Expo Router 默认包含所有路由。添加 Stack.Protected 会为这些屏幕创建例外。 */} </Stack> ); }

在这个示例中,/private 路由无法访问,因为 guard 为 false。当用户尝试访问 /private 时,他们会被重定向到锚点路由,也就是 index 屏幕。

此外,如果用户当前位于 /private/page,并且 guard 条件变为 false,他们也会自动被重定向。

当某个屏幕的 guardtrue 改为 false 时,它的所有历史记录条目都会从导航历史中移除。

多个受保护屏幕

在 Expo Router 中,一个屏幕在同一时间只能存在于一个活动路由组中

你应该只在最合适的组或堆栈中声明一次屏幕。如果某个屏幕的可用性取决于逻辑,请将其包裹在条件组中,而不是重复声明该屏幕。

src/app/_layout.tsx
import { Stack } from 'expo-router'; const isLoggedIn = true; const isAdmin = true; export function AppLayout() { return ( <Stack> <Stack.Protected guard={true}> <Stack.Screen name="profile" /> </Stack.Protected> <Stack.Screen name="profile" /> // ❌ 不允许:重复的屏幕 </Stack> ); }

嵌套受保护屏幕

受保护屏幕可以嵌套,以定义分层访问控制逻辑。

src/app/_layout.tsx
import { Stack } from 'expo-router'; const isLoggedIn = true; const isAdmin = true; export function AppLayout() { return ( <Stack> <Stack.Protected guard={isLoggedIn}> <Stack.Protected guard={isAdmin}> <Stack.Screen name="private" /> </Stack.Protected> <Stack.Screen name="about" /> </Stack.Protected> </Stack> ); }

在这种情况下:

  • 仅当用户已登录且是管理员时,/private 才受保护。
  • /about 对任何已登录用户受保护。

回退到特定屏幕

你可以配置导航器在访问被拒绝时回退到某个特定屏幕。

src
app
  _layout.tsx
  index.tsx
  about.tsx
  login.tsx
  private
   _layout.tsx
   index.tsx
   page.tsx
src/app/_layout.tsx
import { Stack } from 'expo-router'; const isLoggedIn = false; export function AppLayout() { return ( <Stack> <Stack.Protected guard={isLoggedIn}> <Stack.Screen name="index" /> <Stack.Screen name="private" /> </Stack.Protected> <Stack.Screen name="login" /> </Stack> ); }

在上面的示例中,由于 index 屏幕受保护且 guardfalse,路由器会重定向到第一个可用屏幕 —— login

Tabs 和 Drawer

受保护路由也适用于 TabsDrawer 导航器。

src/app/_layout.tsx
import { Tabs } from 'expo-router'; const isLoggedIn = false; export default function TabLayout() { return ( <Tabs> <Tabs.Screen name="index" options={{ tabBarLabel: 'Home' }} /> <Tabs.Protected guard={isLoggedIn}> <Tabs.Screen name="private" options={{ tabBarLabel: 'Private' }} /> <Tabs.Screen name="profile" options={{ tabBarLabel: 'Profile' }} /> </Tabs.Protected> <Tabs.Protected guard={!isLoggedIn}> <Tabs.Screen name="login" options={{ tabBarLabel: 'Login' }} /> </Tabs.Protected> </Tabs> ); }

自定义导航器

Protected 也可用于使用 withLayoutContext 钩子的自定义导航器

静态渲染注意事项

受保护屏幕仅在客户端进行评估。在静态站点生成期间,不会为受保护路由创建任何 HTML 文件。不过,如果用户知道这些路由的 URL,他们仍然可以直接请求对应的 HTML 或 JavaScript 文件。受保护屏幕不能替代服务器端身份验证或访问控制。