受保护的路由

编辑

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


受保护的路由在 SDK 53 及更高版本中可用。
观看:使用受保护的路由
观看:使用受保护的路由

概述

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

app
_layout.tsx
index.tsx
about.tsx
login.tsx只有在未认证时可用
private
  _layout.tsx只有在认证时可用
  index.tsx
  page.tsx
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 中,一个屏幕 一次只能存在于一个活动路由组中

您应仅在最合适的组或堆栈中声明一个屏幕一次。如果一个屏幕的可用性依赖于逻辑,则应将其包装在条件组中,而不是重复屏幕。

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> ); }

嵌套受保护的屏幕

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

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 对任何已登录用户受到保护。

回退到特定屏幕

您可以配置导航器,在拒绝访问时回退到特定屏幕。

app
_layout.tsx
index.tsx
about.tsx
login.tsx
private
  _layout.tsx
  index.tsx
  page.tsx
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 屏幕是受保护的而 protectedfalse,路由器重定向到第一个可用屏幕 — login

标签和抽屉

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

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 文件。受保护的屏幕不能替代服务器端的身份验证或访问控制。