页面间导航

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


在你的应用中创建几个页面并设置好布局后,就可以开始在它们之间导航了。Expo Router 中的导航与 React Navigation 非常相似,但所有页面默认都有一个 URL,因此我们可以创建链接并使用这些 URL,以熟悉的 Web 模式在应用中移动。

¥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';

export default function Home() {
  const router = useRouter();

  return <Button onPress={() => router.navigate('/about')}>Go to About</Button>;
}

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 或相对于应用目录的位置来引用页面。查看以下文件结构以及如何导航到每个页面:

¥With Expo Router, you refer to pages by their URL, or their position relative to the app directory. Check out the following file structure and how you would navigate to each page:

app
index.tsxrouter.navigate("/")
about.tsxrouter.navigate("/about")
profile
  index.tsxrouter.navigate("/profile")
  friends.tsxrouter.navigate("/profile/friends")

链接和按钮

¥Links and buttons

在 Expo Router 中链接到页面的典型方式是使用类似 Web 应用的链接。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:

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 属性的组件:

¥By default, Links can only wrap Text components. You can use Pressable or other components that support onPress and onClick props inside a link with the asChild prop:

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

相对路由

¥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

Using dynamic routes with Expo Router
Using dynamic routes with Expo Router

Learn how to make a segment of a route dynamic.

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

¥Dynamic routes can be linked to with their full URL, or by passing a params object.

考虑以下文件结构:

¥Consider the following file structure:

app
user
  [id].tsx

以下每个链接都将导航到同一页面:

¥Each of these links will navigate to the same page:

app/index.tsx
import { Link } from 'expo-router';
import { View, Pressable } 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('/user', { id: 'bacon' })}>
        <Text>View user (imperative)</Text>
      </Pressable>
    </View>
  );
}

传递查询参数

¥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

接收页面可以通过 useLocalSearchParams 钩子访问链接 URL 中的所有变量。此钩子返回一个包含所有 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>

重定向

¥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" />;
}

深层链接

¥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.

在网页端,深度链接非常简单,只需在网页浏览器中导航到特定的 URL 即可。在移动设备上,你可以在 应用配置 文件中定义一个 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.

假设你的 schememyapp,以下是如何从网页或其他应用链接到你应用中页面的示例:

¥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:

app
about.tsxmyapp://about
profile
  index.tsxmyapp://profile
users
  [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:

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

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

¥stack is a stack navigator, and /stack/index is always the first route in the stack.

为了确保即使用户深度链接到 /stack/second/stack/index 也始终优先加载,你可以在 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 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.

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

¥So, if 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 来解决。