在 Expo Router 中页面间导航
了解在 Expo Router 中链接和导航到页面的不同方式。
一旦你的应用有了几页并且布局设置完成,就可以开始在它们之间导航了。Expo Router 的导航方式与 React Navigation 很相似,但由于所有页面默认都有 URL,我们可以创建链接,并使用这些 URL 通过熟悉的网页模式在应用中移动。
🌐 Once you have a few pages in your app and their layouts setup, it's time to start navigating between them. Navigation in Expo Router works a lot like React Navigation, but with all pages having a URL by default, we can create links and use these URLs to move about our app using familiar web patterns.
useRouter 的原生导航基础
🌐 Native navigation basics with useRouter
就像在 React Navigation 中一样,你可以从 onPress 处理函数中调用一个函数来导航到另一个页面。在 Expo Router 中,你可以使用 useRouter 钩子来访问导航函数:
🌐 Like in React Navigation, you can call a function from an onPress handler to navigate to another page. In Expo Router, you can use the useRouter hook to access navigation functions:
import { useRouter } from 'expo-router'; import { Button } from 'react-native'; export default function Home() { const router = useRouter(); return <Button title="进入关于" onPress={() => router.navigate('/about')} />; }
Expo Router 应用默认使用堆栈导航,在这种导航方式中,跳转到新路由会将屏幕推入堆栈,而返回则会将其从堆栈中弹出。通常,你会想使用 router.navigate 函数。它要么将新页面推入堆栈,要么回到堆栈中已存在的路由。不过,你也可以调用 router.push 明确地将新页面推入堆栈,调用 router.back 返回上一页,或者调用 router.replace 替换堆栈中的当前页面。
🌐 Expo Router apps default to stack navigation, where navigating to a new route pushes a screen onto a stack, and backing out of that route pops it off the stack. Usually, you would want to use the router.navigate function. This will either push a new page onto the stack or unwind to an existing route on the stack. However, you can also call router.push to explicitly push a new page onto the stack, router.back to go back to the previous page, or router.replace to replace the current page on the stack.
使用 Expo Router,你可以通过页面的 URL,或相对于 src/app 目录的位置来引用页面。查看以下文件结构以及你如何导航到每个页面:
🌐 With Expo Router, you refer to pages by their URL, or their position relative to the src/app directory. Check out the following file structure and how you would navigate to each page:
srcappindex.tsxrouter.navigate("/")about.tsxrouter.navigate("/about")profileindex.tsxrouter.navigate("/profile")friends.tsxrouter.navigate("/profile/friends")了解如何使用所有可用于命令式导航的功能。
链接和按钮
🌐 Links and buttons
在 Expo Router 中链接到页面的典型方式是像网络应用一样使用链接。Expo Router 有一个 Link 组件用于在页面之间导航,其中 href 是你在 router.navigate 中会使用的相同路由:
🌐 The typical way to link to a page in Expo Router is to use links like web apps. Expo Router has a Link component for navigating between pages, where the href is the same route you would use in router.navigate:
import { View } from 'react-native'; import { Link } from 'expo-router'; export default function Page() { return ( <View> <Link href="/about">About</Link> </View> ); }
默认情况下,Link 组件会将其子元素渲染在一个 <Text> 元素内。这意味着非文本子元素(例如 View)可能会有意想不到的布局行为。要完全控制布局,请将 asChild 属性与 Pressable 或其他接受 onPress/onClick 属性的组件一起使用:
🌐 By default, the Link component renders its children inside a <Text> element. This means non-text children (such as View) may have unexpected layout behavior. To have full control over the layout, use the asChild prop with a Pressable or another component that accepts onPress/onClick props:
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 上为链接添加预览。
相对路由
🌐 Relative routes
你不必总是使用路由的绝对路径。使用以 ./(表示当前目录)或 ../(表示父目录)开头的路径,将相对于当前路由进行导航。
🌐 You don't always have to use the absolute path to a route. Using paths that start with ./ (for the current directory) or ../ (for the parent directory) will navigate relative to the current route.
相对 URL 是以 ./ 为前缀的 URL,例如 ./article 或 ./article/。相对 URL 会相对于当前渲染的屏幕进行解析。
🌐 A relative URL is a URL prefix with ./, such as ./article, or ./article/. Relative URLs are resolved relative to the current rendered screen.
<Link href="./article">Go to article</Link>
router.navigate('./article');
动态路由和 URL 参数
🌐 Dynamic routes and URL parameters

学习如何使路径的某一段变得动态。
动态路由可以通过完整的 URL 进行链接,或者通过传递一个 params 对象。
🌐 Dynamic routes can be linked to with their full URL, or by passing a params object.
考虑以下文件结构:
🌐 Consider the following file structure:
srcappuser[id].tsx以下每个链接都将导航到同一页面:
🌐 Each of these links will navigate to the same page:
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 参数指南 中找到它们。
传递查询参数
🌐 Passing query parameters
你可以在链接 URL 中直接指定查询参数,也可以作为 params 对象中的附加参数。任何与动态路由变量名称不匹配的参数都等同于查询参数。
🌐 You can specify query parameters in the link URL itself, or as additional parameters in the params object. Any parameters that don't match the name of the dynamic route variable are equivalent to query parameters.
<Link href="/users?limit=20">View users</Link> <Link href={{ pathname: '/users', params: { limit: 20 } }}> View users </Link>
在目标页面中使用动态路由变量和查询参数
🌐 Using dynamic route variables and query parameters in the destination page
链接 URL 中的所有变量都可以通过 useLocalSearchParams 钩子在接收页面访问。该钩子返回一个包含所有 URL 参数的对象,包括作为 params 传递的参数。
🌐 All variables in the link URL are accessible to the receiving page via the useLocalSearchParams hook. This hook returns an object with all the URL parameters, including those passed as params.
例如,如果你有如下链接:
🌐 For example, if you have a link like this:
<Link href="/users?limit=20">View users</Link>
然后,你可以像这样在另一端读取参数:
🌐 Then you can read the parameters on the other end like this:
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> ); }
无需导航即可更新查询参数
🌐 Updating query parameters without navigating
查询参数可以在不跳转到新页面的情况下更新。这可以通过使用与当前页面相同 URL 但带有更新查询参数的 Link 实现,或者通过命令式方式实现。
🌐 Query parameters can be updated without navigating to a new page. This can be done with a Link that uses the same URL as the current page, but with updated query parameters, or imperatively.
<Link href="/users?limit=50">View more users</Link> <Pressable onPress={() => router.setParams({ limit: 50 })}> <Text>View more users</Text> </Pressable>
了解更多关于如何在 Expo Router 中设置和使用 URL 参数的信息。
重定向
🌐 Redirects
你可以使用 Redirect 组件从页面或布局立即重定向到另一个路由。它的功能类似于 replace 的命令式导航功能。重定向会跳转到新路由,而不会渲染当前页面。
🌐 You can immediately redirect to another route from a page or layout with the Redirect component. This functions like the replace imperative navigation function. A redirect will navigate to the new route without rendering the current page.
import { Redirect } from 'expo-router'; export default function Page() { return <Redirect href="/about" />; }
预加载
🌐 Prefetching
<Link /> 组件上的 prefetch 属性在组件渲染时启用目标屏幕的预取。这通过提前准备屏幕来实现更快速的导航。
🌐 The prefetch prop on a <Link /> component enables prefetching of the target screen when the component is rendered. This allows for faster navigation by preparing the screen in advance.
import { Link } from 'expo-router'; export default function Page() { return <Link href="/about" prefetch />; }
当设置 prefetch 时,Expo Router 会尝试在屏幕外渲染目标屏幕。具体行为取决于所使用的导航器类型:
🌐 When prefetch is set, Expo Router will attempt to render the target screen off-screen. The exact behavior depends on the type of navigator used:
- Expo 路由导航器:在屏幕外渲染目标屏幕以实现预加载。
- 自定义导航器:可能会以不同的方式实现预取,或者根本不支持预取。
当屏幕在堆栈导航器中预加载时,它会受到一些限制:
🌐 When a screen is preloaded in a stack navigator, it will have a few limitations:
- 它无法使用
router这个命令式 API。 - 无法使用
useNavigation().setOptions()更新选项 - 它无法监听导航器中的事件(例如 focus、tabPress 等)。
一旦你导航到屏幕,导航对象将会更新。所以如果你在 useEffect 钩子中有一个事件监听器,并且依赖了导航对象,那么在导航到屏幕时,它会添加任何监听器:
🌐 The navigation object will be updated once you navigate to the screen. So if you have an event listener in a useEffect hook, and have a dependency on navigation, it will add any listeners when the screen is navigated to:
const navigation = useNavigation(); useEffect(() => { const unsubscribe = navigation.addListener('tabPress', () => { // do something }); return () => { unsubscribe(); }; }, [navigation]);
类似地,对于调度操作或更新选项,你可以在执行此操作之前检查屏幕是否已聚焦:
🌐 Similarly, for dispatching actions or updating options, you can check if the screen is focused before doing so:
const navigation = useNavigation(); if (navigation.isFocused()) { navigation.setOptions({ title: 'Updated title' }); }
欲了解更多信息,请参考 React Navigation 预加载文档
🌐 For more information, refer to the React Navigation preload docs
深层链接
🌐 Deep links
深度链接是指 URL 打开应用中的特定页面。Expo Router 默认支持深度链接,因此你可以使用应用外的 URL 链接到应用中的任何页面,就像在应用内部使用 Link 一样。这对于分享应用中特定页面的链接特别有用。
🌐 Deep linking is when a URL opens a specific page in your app. Expo Router supports deep linking by default, so you can link to any page in your app with a URL from outside of your app, as you would inside your app with Link. This is especially useful for sharing links to specific pages in your app.
在网页上,深度链接就像在浏览器中导航到特定网址一样简单。在移动端,你需要在你的应用配置文件中定义一个 scheme,这将成为进入你的应用的深度链接前缀。
🌐 On web, deep linking is as simple as navigating to that specific URL in your web browser. On mobile, you define a scheme in your app config file, and this becomes the prefix for deep links into your app.
假设你的 scheme 是 myapp,以下是一些示例,说明如何从网页或其他应用链接到你应用中的页面:
🌐 Assuming your scheme is myapp, here are some examples of how you would link to a page in your app from a web page or another app:
srcappabout.tsxmyapp://aboutprofileindex.tsxmyapp://profileusers[username].tsxmyapp://users/evanbacon通过应用链接和通用链接,你还可以使用 https URL 链接到你的应用。有关更多信息,请参阅 通用链接。
🌐 With app links and universal links, you can also link to your app with an https URL. For more information, see Universal linking.
初始路由
🌐 Initial routes
当打开指向你应用中某个页面的深层链接时,你可能希望返回导航的行为就像用户从首页导航到该页面一样。为此,你可以指定一个 initialRouteName 配置,该配置定义了在加载深层链接页面之前应加载的布局页面。
🌐 When opening a deep link to a page in your app, you will likely want back navigation to work as if the user navigated to the page from your home page. To do this, you can specify an initialRouteName configuration, which defines the page in a layout that should be loaded before the deep linked page.
考虑以下文件结构:
🌐 Consider the following file structure:
srcappindex.tsxstackindex.tsxsecond.tsx_layout.tsxstack 是一个堆栈导航器,而 /stack/index 总是堆栈中的第一个路由。
为了确保即使用户通过深层链接访问 /stack/second,/stack/index 也总是首先加载,你可以在 src/app/stack/_layout.tsx 中设置 initialRouteName:
🌐 To ensure that /stack/index is always loaded first, even if the user deep links to /stack/second, you can set the initialRouteName in src/app/stack/_layout.tsx:
export const unstable_settings = { // Ensure any route can link back to `/` initialRouteName: 'index', };
默认情况下,initialRouteName 仅在深度链接时考虑,而不会在应用内导航时使用。但是,你可以在 Link 上使用 withAnchor 属性,以在直接导航到应用内的另一个堆栈时强制加载初始路由。
🌐 By default, the initialRouteName is only considered when deep linking and not during navigation within your app. However, you can use the withAnchor prop on Link to force the initial route to be loaded when navigating directly into another stack inside your app.
所以,如果 src/app/index.tsx 包含指向 /stack/second 的链接,请添加 withAnchor 属性以确保首先加载 /stack/index,这将导致用户在从 /stack/second 按下返回按钮时返回到 /stack/index:
🌐 So, if src/app/index.tsx contained a link to /stack/second, add the withAnchor prop to ensure that /stack/index is loaded first, which will cause the user to go back to /stack/index when they press the back button from /stack/second:
<Link href="/stack/second" withAnchor> Go to second </Link>
信息 如果在测试深度链接时缺少返回按钮,通常可以通过设置
initialRouteName来解决此问题。