在 Expo Router 中页面之间导航

编辑

了解在 Expo Router 中链接和导航到页面的不同方式。


一旦您在应用程序中设置了一些页面及其布局,就该开始在它们之间导航了。Expo Router 中的导航与 React Navigation 类似,但由于所有页面默认都具有 URL,我们可以创建链接并使用这些 URL 以熟悉的 Web 模式在我们的应用程序中移动。

使用 useRouter 的原生导航基础

与 React Navigation 一样,您可以从 onPress 处理程序中调用函数以导航到另一个页面。在 Expo Router 中,您可以使用 useRouter 钩子访问导航函数:

import { useRouter } from 'expo-router'; import { Button } from 'react-native'; export default function Home() { const router = useRouter(); return <Button title="Go to About" onPress={() => router.navigate('/about')} />; }

Expo Router 应用程序默认使用堆栈导航,当导航到新路线时,会将一个屏幕推入堆栈,并在退出该路线时将其从堆栈弹出。通常,您想使用 router.navigate 函数。这将把新页面推入堆栈或者退回到堆栈中的现有路线。不过,您也可以调用 router.push 明确地将新页面推入堆栈,使用 router.back 返回到前一个页面,或使用 router.replace 来替换堆栈中的当前页面。

在 Expo Router 中,您可以通过其 URL 或相对于 app 目录的位置来引用页面。查看以下文件结构以及您如何导航到每个页面:

app
index.tsxrouter.navigate("/")
about.tsxrouter.navigate("/about")
profile
  index.tsxrouter.navigate("/profile")
  friends.tsxrouter.navigate("/profile/friends")
Router 复杂导航 API 参考

学习如何使用所有可用于复杂导航的函数。

链接和按钮

在 Expo Router 中链接到页面的典型方式是像 Web 应用程序一样使用链接。Expo Router 有一个 Link 组件用于在页面之间导航,href 就是您在 router.navigate 中使用的相同路由:

app/index.tsx
import { View } from 'react-native'; import { Link } from 'expo-router'; export default function Page() { return ( <View> <Link href="/about">About</Link> </View> ); }

默认情况下,链接只能包裹 Text 组件。您可以在使用 asChild 属性的链接内使用 Pressable 或其他支持 onPressonClick 属性的组件:

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> ); }
链接 API 参考

了解使用链接进行导航时可用的选项。

链接预览

了解如何在使用 Expo Router 的 iOS 上为您的链接添加预览。

相对路由

您并不总是需要使用路由的绝对路径。使用以 ./(当前目录)或 ../(上级目录)开头的路径将相对于当前路由进行导航。

相对 URL 是带有 ./ 的 URL 前缀,例如 ./article./article/。相对 URL 相对于当前渲染的屏幕解析。

<Link href="./article">Go to article</Link>
router.navigate('./article');

动态路由和 URL 参数

在 Expo Router 中使用动态路由
在 Expo Router 中使用动态路由

学习如何使路由的一个部分动态。

动态路由可以通过完整 URL 链接,或通过传递 params 对象。

考虑以下文件结构:

app
user
  [id].tsx

每个链接都将导航到相同的页面:

app/index.tsx
import { Link, router } from 'expo-router'; import { View, Pressable, Text } from 'react-native'; export default function Page() { return ( <View> <Link href="/user/bacon"> View user (id inline) </Link> <Link href={{ pathname: '/user/[id]', params: { id: 'bacon' } }} > View user (id in params in href) </Link> <Pressable onPress={() => router.navigate({ pathname: '/user/[id]', params: { id: 'bacon' } }) } > <Text>View user (imperative)</Text> </Pressable> </View> ); }
一些参数保留供 Expo Router 和 React Navigation 内部使用。您可以在 使用 URL 参数指南 中找到它们。

传递查询参数

您可以在链接 URL 本身中指定查询参数,或作为 params 对象中的附加参数。任何与动态路由变量名称不匹配的参数都相当于查询参数。

<Link href="/users?limit=20">查看用户</Link> <Link href={{ pathname: '/users', params: { limit: 20 } }}> View users </Link>

在目标页面中使用动态路由变量和查询参数

链接 URL 中的所有变量都可以通过 useLocalSearchParams 钩子访问到接收页面。该钩子返回一个包含所有 URL 参数的对象,包括作为 params 传递的那些。

例如,如果您有这样的链接:

<Link href="/users?limit=20">View users</Link>

那么您可以在另一端这样读取参数:

import { useLocalSearchParams } from 'expo-router'; import { View, Text } from 'react-native'; export default function Users() { const { id, limit } = useLocalSearchParams(); return ( <View> <Text>User ID: {id}</Text> <Text>Limit: {limit}</Text> </View> ); }

无需导航即可更新查询参数

可以无需导航到新页面而更新查询参数。这可以通过使用与当前页面相同的 URL 的 Link 来实现,但更新了查询参数,或者以强制方式实现。

<Link href="/users?limit=50">View more users</Link> <Pressable onPress={() => router.setParams({ limit: 50 })}> <Text>View more users</Text> </Pressable>
使用 URL 参数

深入了解如何在 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 导航器:在屏幕外渲染目标屏幕以启用预加载。
  • 自定义导航器:可能以不同方式实现预取或根本不支持它。

当屏幕在堆栈导航器中预加载时,它将有一些限制:

  • 它不能使用强制性的 router API。
  • 它不能使用 useNavigation().setOptions() 更新选项。
  • 它不能监听来自导航器的事件(例如焦点、tabPress 等)。

在您导航到屏幕时,导航对象将被更新。因此,如果您在 useEffect 钩子中有事件监听器并依赖于导航,它将在导航到屏幕时添加任何监听器:

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 默认支持深度链接,因此您可以使用外部 URL 链接到应用程序中的任何页面,就像您在应用程序内部使用 Link 一样。这对于共享指向应用程序中特定页面的链接尤其有用。

在 Web 上,深度链接和在 Web 浏览器中导航到该特定 URL 一样简单。在移动设备上,您需要在 应用配置 文件中定义一个 scheme,该 scheme 成为深度链接进入您的应用程序的前缀。

假设您的 schememyapp,以下是一些示例,说明如何从网页或其他应用程序链接到您应用程序中的页面:

app
about.tsxmyapp://about
profile
  index.tsxmyapp://profile
users
  [username].tsxmyapp://users/evanbacon

使用应用链接和通用链接,您还可以使用 https URL 链接到您的应用程序。有关更多信息,请参见 通用链接

初始路由

在打开深度链接到您应用程序中的页面时,您可能希望后退导航的表现与用户从您的主页导航到该页面时相同。要做到这一点,您可以指定一个 initialRouteName 配置,定义在深度链接页面之前应该加载的布局页面。

考虑以下文件结构:

app
index.tsx
stack
  index.tsx
  second.tsx
  _layout.tsx

stack 是一个堆栈导航器,而 /stack/index 始终是堆栈中的第一个路由。

为确保即使用户深度链接到 /stack/second/stack/index 也始终首先加载,您可以在 app/stack/_layout.tsx 中设置 initialRouteName

export const unstable_settings = { // 确保任何路由都可以链接回 `/` initialRouteName: 'index', };

默认情况下,仅在深度链接时考虑 initialRouteName,而在应用程序内部导航时不考虑。不过,您可以在 Link 上使用 withAnchor 属性,以强制在直接导航到应用程序中的另一个堆栈时加载初始路由。

因此,如果 app/index.tsx 中包含一个指向 /stack/second 的链接,请添加 withAnchor 属性,以确保首先加载 /stack/index,这将导致用户在从 /stack/second 按下后退按钮时返回 /stack/index

<Link href="/stack/second" withAnchor> Go to second </Link>
如果在测试深度链接时缺少后退按钮,通常可以通过设置 initialRouteName 来解决。