在 Expo Router 中在页面之间导航
编辑页面
了解在 Expo Router 中链接到页面和导航到页面的不同方式。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
一旦你的应用中有了几个页面并完成了它们的布局设置,就该开始在它们之间进行导航了。Expo Router 中的导航很像 React Navigation,但由于所有页面默认都有一个 URL,我们可以创建链接,并使用这些 URL 通过熟悉的 Web 模式在应用中移动。
使用 useRouter 的原生导航基础
和 React Navigation 一样,你可以在 onPress 处理函数中调用一个函数来导航到另一个页面。在 Expo Router 中,你可以使用 useRouter Hook 来访问导航函数:
import { useRouter } from 'expo-router'; import { Button } from 'react-native'; export default function Home() { const router = useRouter(); return <Button title="前往 About" onPress={() => router.navigate('/about')} />; }
Expo Router 应用默认使用栈导航,其中导航到新路由会将一个屏幕压入栈中,而从该路由返回则会将其从栈中弹出。通常,你会希望使用 router.navigate 函数。它要么会将新页面压入栈中,要么会回退到栈中已存在的路由。不过,你也可以调用 router.push 来显式地将新页面压入栈中,调用 router.back 返回上一页,或者调用 router.replace 替换栈中的当前页面。
在 Expo Router 中,你可以通过页面的 URL,或者它们相对于 src/app 目录的位置来引用页面。看看下面的文件结构,以及你会如何导航到每个页面:
srcappindex.tsxrouter.navigate("/")about.tsxrouter.navigate("/about")profileindex.tsxrouter.navigate("/profile")friends.tsxrouter.navigate("/profile/friends")了解如何使用命令式导航可用的所有函数。
链接和按钮
在 Expo Router 中,链接到页面的常见方式是像 Web 应用那样使用链接。Expo Router 提供了一个 Link 组件用于在页面之间导航,其中 href 与你在 router.navigate 中使用的路由相同:
import { View } from 'react-native'; import { Link } from 'expo-router'; export default function Page() { return ( <View> <Link href="/about">关于</Link> </View> ); }
默认情况下,Link 组件会将其子元素渲染在一个 <Text> 元素内。这意味着非文本子元素(例如 View)可能会出现意外的布局行为。若要完全控制布局,请将 asChild 属性与 Pressable 或其他接受 onPress/onClick 属性的组件一起使用:
import { Pressable, Text } from 'react-native'; import { Link } from 'expo-router'; export default function Page() { return ( <Link href="/other" asChild> <Pressable> <Text>Home</Text> </Pressable> </Link> ); }
了解在使用链接进行导航时可用的选项。
了解在使用 Expo Router 时,如何在 iOS 上为你的链接添加预览。
相对路由
你并不总是必须使用路由的绝对路径。使用以 ./(表示当前目录)或 ../(表示父目录)开头的路径,将会相对于当前路由进行导航。
相对 URL 是以 ./ 开头的 URL 前缀,例如 ./article 或 ./article/。相对 URL 会相对于当前渲染的屏幕进行解析。
<Link href="./article">前往文章</Link>
router.navigate('./article');
动态路由和 URL 参数

了解如何使路由的某个段成为动态的。
动态路由可以通过完整 URL 进行链接,或者通过传递一个 params 对象来链接。
请考虑以下文件结构:
srcappuser[id].tsx下面这些链接都会导航到同一个页面:
import { Link, router } from 'expo-router'; import { View, Pressable, Text } from 'react-native'; export default function Page() { return ( <View> <Link href="/user/bacon"> 查看用户(id 内联) </Link> <Link href={{ pathname: '/user/[id]', params: { id: 'bacon' } }} > 查看用户(id 作为 href 中 params 的值) </Link> <Pressable onPress={() => router.navigate({ pathname: '/user/[id]', params: { id: 'bacon' } }) } > <Text>查看用户(命令式)</Text> </Pressable> </View> ); }
某些参数保留供 Expo Router 和 React Navigation 内部使用。你可以在 使用 URL 参数指南 中找到它们。
传递查询参数
你可以在链接 URL 本身中指定查询参数,也可以在 params 对象中作为额外参数传入。任何与动态路由变量名称不匹配的参数都等同于查询参数。
<Link href="/users?limit=20">查看用户</Link> <Link href={{ pathname: '/users', params: { limit: 20 } }}> 查看用户 </Link>
在目标页面中使用动态路由变量和查询参数
链接 URL 中的所有变量都可以通过 useLocalSearchParams hook 被接收页面访问。这个 hook 会返回一个包含所有 URL 参数的对象,包括通过 params 传入的参数。
例如,如果你有如下链接:
<Link href="/users?limit=20">查看用户</Link>
那么你可以像这样在另一端读取这些参数:
import { useLocalSearchParams } from 'expo-router'; import { View, Text } from 'react-native'; export default function Users() { const { id, limit } = useLocalSearchParams(); return ( <View> <Text>用户 ID:{id}</Text> <Text>限制:{limit}</Text> </View> ); }
在不导航的情况下更新查询参数
查询参数可以在不导航到新页面的情况下更新。这可以通过使用与当前页面相同 URL 的 Link 但更新查询参数来实现,也可以通过命令式方式实现。
<Link href="/users?limit=50">查看更多用户</Link> <Pressable onPress={() => router.setParams({ limit: 50 })}> <Text>查看更多用户</Text> </Pressable>
了解更多关于如何在 Expo Router 中设置和使用 URL 参数。
重定向
你可以使用 Redirect 组件立即从页面或布局重定向到另一个路由。其功能类似于 replace 命令式导航函数。重定向会在不渲染当前页面的情况下导航到新路由。
import { Redirect } from 'expo-router'; export default function Page() { return <Redirect href="/about" />; }
预取
<Link /> 组件上的 prefetch 属性会在组件渲染时启用目标屏幕的预取。这通过提前准备屏幕来实现更快的导航。
import { Link } from 'expo-router'; export default function Page() { return <Link href="/about" prefetch />; }
当设置了 prefetch 时,Expo Router 会尝试在屏幕外渲染目标屏幕。具体行为取决于所使用的导航器类型:
- Expo Router 导航器:在屏幕外渲染目标屏幕以启用预加载。
- 自定义导航器:可能以不同方式实现预取,或者根本不支持。
当某个屏幕在栈导航器中被预加载时,它会有一些限制:
- 不能使用命令式
routerAPI。 - 不能通过
useNavigation().setOptions()更新选项 - 不能监听来自导航器的事件(例如 focus、tabPress 等)。
当你导航到该屏幕后,navigation 对象会更新。因此,如果你在 useEffect hook 中有一个事件监听器,并且依赖于 navigation,那么当屏幕被导航到时,它会添加任何监听器:
const navigation = useNavigation(); useEffect(() => { const unsubscribe = navigation.addListener('tabPress', () => { // 执行某些操作 }); return () => { unsubscribe(); }; }, [navigation]);
同样地,对于分发操作或更新选项,你可以在执行之前检查屏幕是否处于聚焦状态:
const navigation = useNavigation(); if (navigation.isFocused()) { navigation.setOptions({ title: 'Updated title' }); }
有关更多信息,请参阅 React Navigation 预加载文档
深度链接
深度链接是指某个 URL 在你的应用中打开特定页面。Expo Router 默认支持深度链接,因此你可以像在应用内使用 Link 一样,从应用外部通过 URL 链接到应用中的任意页面。这对于分享应用中特定页面的链接尤其有用。
在 web 上,深度链接就像在浏览器中导航到该特定 URL 一样简单。在移动端,你需要在 应用配置 文件中定义一个 scheme,它将成为进入你应用的深度链接前缀。
假设你的 scheme 是 myapp,以下是一些如何从网页或另一个应用链接到你应用中页面的示例:
srcappabout.tsxmyapp://aboutprofileindex.tsxmyapp://profileusers[username].tsxmyapp://users/evanbacon通过 app links 和 universal links,你也可以使用 https URL 链接到你的应用。有关更多信息,请参阅 通用链接。
初始路由
当打开指向你应用中某个页面的深度链接时,你通常会希望返回导航的行为与用户从首页导航到该页面时一样。为此,你可以指定 initialRouteName 配置,它定义了某个布局中应在深度链接页面之前加载的页面。
考虑以下文件结构:
srcappindex.tsxstackindex.tsxsecond.tsx_layout.tsxstack 是一个栈导航器,而 /stack/index 始终是栈中的第一个路由。
为了确保 /stack/index 始终最先加载,即使用户深度链接到 /stack/second,你也可以在 src/app/stack/_layout.tsx 中设置 initialRouteName:
export const unstable_settings = { // 确保任何路由都可以链接回 `/` initialRouteName: 'index', };
默认情况下,initialRouteName 只在深度链接时被考虑,而不会在应用内部导航时生效。不过,你可以在 Link 上使用 withAnchor 属性,强制在直接导航到应用内另一个栈时先加载初始路由。
因此,如果 src/app/index.tsx 中包含一个指向 /stack/second 的链接,添加 withAnchor 属性可确保 /stack/index 先被加载,这样当用户从 /stack/second 按下返回按钮时,就会回到 /stack/index:
<Link href="/stack/second" withAnchor> 前往第二页 </Link>
如果你在测试深度链接时缺少返回按钮,这通常可以通过设置initialRouteName来修复。