框架
版本

导航

一切都是相对的

信不信由你,应用程序中的每个导航都是相对的,即使您没有使用显式的相对路径语法(../../somewhere)。每当点击链接或进行命令式导航调用时,您总是有一个起始路径和一个目标路径,这意味着您正在一个路由导航另一个路由。

TanStack Router 在每次导航中都牢记这种相对导航的恒定概念,因此您会在 API 中经常看到两个属性:

  • from - 起始路由路径
  • to - 目标路由路径

⚠️ 如果没有提供 from 路由路径,路由器将假设您从根 / 路由导航,并且只自动完成绝对路径。毕竟,您需要知道自己从哪里来才能知道要去哪里 😉。

共享导航 API

TanStack Router 中的每个导航和路由匹配 API 都使用相同的核心接口,根据 API 的不同只有细微差别。这意味着您可以一次学会导航和路由匹配,并在整个库中使用相同的语法和概念。

ToOptions 接口

这是在每个导航和路由匹配 API 中使用的核心 ToOptions 接口:

ts
type ToOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = {
  // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
  from: string
  // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
  to: string
  // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
  params:
    | Record<string, unknown>
    | ((prevParams: Record<string, unknown>) => Record<string, unknown>)
  // `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.
  search:
    | Record<string, unknown>
    | ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
  // `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.
  hash?: string | ((prevHash: string) => string)
  // `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.
  state?:
    | Record<string, any>
    | ((prevState: Record<string, unknown>) => Record<string, unknown>)
}
type ToOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = {
  // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
  from: string
  // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
  to: string
  // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
  params:
    | Record<string, unknown>
    | ((prevParams: Record<string, unknown>) => Record<string, unknown>)
  // `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.
  search:
    | Record<string, unknown>
    | ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
  // `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.
  hash?: string | ((prevHash: string) => string)
  // `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.
  state?:
    | Record<string, any>
    | ((prevState: Record<string, unknown>) => Record<string, unknown>)
}

🧠 Every route object has a to property, which can be used as the to for any navigation or route matching API. Where possible, this will allow you to avoid plain strings and use type-safe route references instead:

tsx
import { Route as aboutRoute } from './routes/about.tsx'

function Comp() {
  return <Link to={aboutRoute.to}>About</Link>
}
import { Route as aboutRoute } from './routes/about.tsx'

function Comp() {
  return <Link to={aboutRoute.to}>About</Link>
}

这是扩展 ToOptions 的核心 NavigateOptions 接口。任何实际执行导航的 API 都将使用此接口:

ts
export type NavigateOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = ToOptions<TRouteTree, TFrom, TTo> & {
  // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
  replace?: boolean
  // `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.
  resetScroll?: boolean
  // `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.
  hashScrollIntoView?: boolean | ScrollIntoViewOptions
  // `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.
  viewTransition?: boolean | ViewTransitionOptions
  // `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.
  ignoreBlocker?: boolean
  // `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.
  reloadDocument?: boolean
  // `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.
  href?: string
}
export type NavigateOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = ToOptions<TRouteTree, TFrom, TTo> & {
  // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
  replace?: boolean
  // `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.
  resetScroll?: boolean
  // `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.
  hashScrollIntoView?: boolean | ScrollIntoViewOptions
  // `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.
  viewTransition?: boolean | ViewTransitionOptions
  // `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.
  ignoreBlocker?: boolean
  // `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.
  reloadDocument?: boolean
  // `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.
  href?: string
}

LinkOptions 接口

在任何实际的 <a> 标签处,扩展 NavigateOptionsLinkOptions 接口都将可用:

tsx
export type LinkOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = NavigateOptions<TRouteTree, TFrom, TTo> & {
  // The standard anchor tag target attribute
  target?: HTMLAnchorElement['target']
  // Defaults to `{ exact: false, includeHash: false }`
  activeOptions?: {
    exact?: boolean
    includeHash?: boolean
    includeSearch?: boolean
    explicitUndefined?: boolean
  }
  // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
  preload?: false | 'intent'
  // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
  preloadDelay?: number
  // If true, will render the link without the href attribute
  disabled?: boolean
}
export type LinkOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = NavigateOptions<TRouteTree, TFrom, TTo> & {
  // The standard anchor tag target attribute
  target?: HTMLAnchorElement['target']
  // Defaults to `{ exact: false, includeHash: false }`
  activeOptions?: {
    exact?: boolean
    includeHash?: boolean
    includeSearch?: boolean
    explicitUndefined?: boolean
  }
  // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
  preload?: false | 'intent'
  // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
  preloadDelay?: number
  // If true, will render the link without the href attribute
  disabled?: boolean
}

导航 API

现在考虑到相对导航和所有接口,让我们谈谈您可以使用的不同类型的导航 API:

  • <Link> 组件
    • 生成一个带有有效 href 的实际 <a> 标签,可以点击甚至 cmd/ctrl + 点击在新标签页中打开
  • useNavigate() 钩子
    • 在可能的情况下,应该使用 Link 组件进行导航,但有时您需要作为副作用的结果进行命令式导航。useNavigate 返回一个可以调用以执行即时客户端导航的函数。
  • <Navigate> 组件
    • 不渲染任何内容并执行即时客户端导航。
  • Router.navigate() 方法
    • 这是 TanStack Router 中最强大的导航 API。与 useNavigate 类似,它进行命令式导航,但在您可以访问路由器的任何地方都可用。

⚠️ 这些 API 都不能替代服务器端重定向。如果您需要在挂载应用程序之前立即将用户从一个路由重定向到另一个路由,请使用服务器端重定向而不是客户端导航。

Link 组件是在应用程序中导航的最常见方式。它渲染一个带有有效 href 属性的实际 <a> 标签,可以点击甚至 cmd/ctrl + 点击在新标签页中打开。它还支持任何正常的 <a> 属性,包括 target 以在新窗口中打开链接等。

除了 LinkOptions 接口外,Link 组件还支持以下属性:

tsx
export type LinkProps<
  TFrom extends RoutePaths<RegisteredRouter['routeTree']> | string = string,
  TTo extends string = '',
> = LinkOptions<RegisteredRouter['routeTree'], TFrom, TTo> & {
  // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  activeProps?:
    | FrameworkHTMLAnchorTagAttributes
    | (() => FrameworkHTMLAnchorAttributes)
  // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  inactiveProps?:
    | FrameworkHTMLAnchorAttributes
    | (() => FrameworkHTMLAnchorAttributes)
}
export type LinkProps<
  TFrom extends RoutePaths<RegisteredRouter['routeTree']> | string = string,
  TTo extends string = '',
> = LinkOptions<RegisteredRouter['routeTree'], TFrom, TTo> & {
  // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  activeProps?:
    | FrameworkHTMLAnchorTagAttributes
    | (() => FrameworkHTMLAnchorAttributes)
  // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  inactiveProps?:
    | FrameworkHTMLAnchorAttributes
    | (() => FrameworkHTMLAnchorAttributes)
}

绝对链接

让我们创建一个简单的静态链接!

tsx
import { Link } from '@tanstack/react-router'

const link = <Link to="/about">About</Link>
import { Link } from '@tanstack/react-router'

const link = <Link to="/about">About</Link>

动态链接

动态链接是包含动态段的链接。例如,指向博客文章的链接可能如下所示:

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
  >
    Blog Post
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
  >
    Blog Post
  </Link>
)

请记住,通常动态段参数是 string 值,但它们也可以是您在路由选项中解析为的任何其他类型。无论哪种方式,类型都将在编译时检查,以确保您传递正确的类型。

相对链接

默认情况下,除非提供 from 路由路径,否则所有链接都是绝对的。这意味着上面的链接将始终导航到 /about 路由,无论您当前在哪个路由上。

如果您想创建相对于当前路由的链接,可以提供 from 路由路径:

tsx
const postIdRoute = createRoute({
  path: '/blog/post/$postId',
})

const link = (
  <Link from={postIdRoute.fullPath} to="../categories">
    Categories
  </Link>
)
const postIdRoute = createRoute({
  path: '/blog/post/$postId',
})

const link = (
  <Link from={postIdRoute.fullPath} to="../categories">
    Categories
  </Link>
)

如上所示,通常提供 route.fullPath 作为 from 路由路径。这是因为 route.fullPath 是一个引用,如果您重构应用程序,它会更新。但是,有时无法直接导入路由,在这种情况下,直接将路由路径作为字符串提供是可以的。它仍然会像往常一样进行类型检查!

搜索参数链接

搜索参数是为路由提供额外上下文的好方法。例如,您可能希望为搜索页面提供搜索查询:

tsx
const link = (
  <Link
    to="/search"
    search={{
      query: 'tanstack',
    }}
  >
    Search
  </Link>
)
const link = (
  <Link
    to="/search"
    search={{
      query: 'tanstack',
    }}
  >
    Search
  </Link>
)

通常还希望更新单个搜索参数而不提供有关现有路由的任何其他信息。例如,您可能希望更新搜索结果的页码:

tsx
const link = (
  <Link
    to="."
    search={(prev) => ({
      ...prev,
      page: prev.page + 1,
    })}
  >
    Next Page
  </Link>
)
const link = (
  <Link
    to="."
    search={(prev) => ({
      ...prev,
      page: prev.page + 1,
    })}
  >
    Next Page
  </Link>
)

搜索参数类型安全

搜索参数是一种高度动态的状态管理机制,因此确保您向搜索参数传递正确的类型很重要。我们将在后面的部分详细了解如何验证和确保搜索参数的类型安全,以及其他出色的功能!

哈希链接

哈希链接是链接到页面特定部分的好方法。例如,您可能希望链接到博客文章的特定部分:

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    hash="section-1"
  >
    Section 1
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    hash="section-1"
  >
    Section 1
  </Link>
)

活动和非活动属性

Link 组件支持两个额外的属性:activePropsinactiveProps。这些属性是返回链接的 activeinactive 状态的额外属性的函数。除了样式和类之外,这里传递的所有属性都将覆盖传递给 Link 的原始属性。任何传递的样式或类都会合并在一起。

这是一个例子:

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    activeProps={{
      style: {
        fontWeight: 'bold',
      },
    }}
  >
    Section 1
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    activeProps={{
      style: {
        fontWeight: 'bold',
      },
    }}
  >
    Section 1
  </Link>
)

data-status 属性

除了 activePropsinactiveProps 属性外,Link 组件还会在处于活动状态时向渲染的元素添加 data-status 属性。根据链接的当前状态,此属性将为 activeundefined。如果您更喜欢使用数据属性而不是属性来设置链接样式,这会很方便。

活动选项

Link 组件带有一个 activeOptions 属性,提供了一些确定链接是否活动的选项。以下接口描述了这些选项:

tsx
export interface ActiveOptions {
  // If true, the link will be active if the current route matches the `to` route path exactly (no children routes)
  // Defaults to `false`
  exact?: boolean
  // If true, the link will only be active if the current URL hash matches the `hash` prop
  // Defaults to `false`
  includeHash?: boolean // Defaults to false
  // If true, the link will only be active if the current URL search params inclusively match the `search` prop
  // Defaults to `true`
  includeSearch?: boolean
  // This modifies the `includeSearch` behavior.
  // If true,  properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.
  // defaults to `false`
  explicitUndefined?: boolean
}
export interface ActiveOptions {
  // If true, the link will be active if the current route matches the `to` route path exactly (no children routes)
  // Defaults to `false`
  exact?: boolean
  // If true, the link will only be active if the current URL hash matches the `hash` prop
  // Defaults to `false`
  includeHash?: boolean // Defaults to false
  // If true, the link will only be active if the current URL search params inclusively match the `search` prop
  // Defaults to `true`
  includeSearch?: boolean
  // This modifies the `includeSearch` behavior.
  // If true,  properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.
  // defaults to `false`
  explicitUndefined?: boolean
}

默认情况下,它将检查结果路径名是否是当前路由的前缀。如果提供了任何搜索参数,它将检查它们是否_包含性地_匹配当前位置中的参数。默认情况下不检查哈希。

例如,如果您在 /blog/post/my-first-blog-post 路由上,以下链接将处于活动状态:

tsx
const link1 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-first-blog-post' }}>
    Blog Post
  </Link>
)
const link2 = <Link to="/blog/post">Blog Post</Link>
const link3 = <Link to="/blog">Blog Post</Link>
const link1 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-first-blog-post' }}>
    Blog Post
  </Link>
)
const link2 = <Link to="/blog/post">Blog Post</Link>
const link3 = <Link to="/blog">Blog Post</Link>

但是,以下链接将不会处于活动状态:

tsx
const link4 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-second-blog-post' }}>
    Blog Post
  </Link>
)
const link4 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-second-blog-post' }}>
    Blog Post
  </Link>
)

某些链接只有在完全匹配时才处于活动状态是很常见的。一个很好的例子是指向主页的链接。在这种情况下,您可以传递 exact: true 选项:

tsx
const link = (
  <Link to="/" activeOptions={{ exact: true }}>
    Home
  </Link>
)
const link = (
  <Link to="/" activeOptions={{ exact: true }}>
    Home
  </Link>
)

这将确保当您在子路由时链接不处于活动状态。

需要注意的其他几个选项:

  • 如果您想在匹配中包含哈希,可以传递 includeHash: true 选项
  • 如果您想在匹配中包含搜索参数,可以传递 includeSearch: false 选项

isActive 传递给子组件

Link 组件接受一个函数作为其子组件,允许您将其 isActive 属性传播给子组件。例如,您可以根据父链接是否处于活动状态来设置子组件的样式:

tsx
const link = (
  <Link to="/blog/post">
    {({ isActive }) => {
      return (
        <>
          <span>My Blog Post</span>
          <icon className={isActive ? 'active' : 'inactive'} />
        </>
      )
    }}
  </Link>
)
const link = (
  <Link to="/blog/post">
    {({ isActive }) => {
      return (
        <>
          <span>My Blog Post</span>
          <icon className={isActive ? 'active' : 'inactive'} />
        </>
      )
    }}
  </Link>
)

链接预加载

Link 组件支持在意图时自动预加载路由(目前是悬停或触摸开始)。这可以在路由器选项中配置为默认值(我们很快会详细讨论),或者通过向 Link 组件传递 preload='intent' 属性。这是一个例子:

tsx
const link = (
  <Link to="/blog/post/$postId" preload="intent">
    Blog Post
  </Link>
)
const link = (
  <Link to="/blog/post/$postId" preload="intent">
    Blog Post
  </Link>
)

启用预加载和相对快速的异步路由依赖项(如果有的话),这个简单的技巧可以用很少的努力提高应用程序的感知性能。

更好的是,通过使用像 @tanstack/query 这样的缓存优先库,预加载的路由将保持存在,并为用户稍后决定导航到该路由时提供 stale-while-revalidate 体验。

链接预加载超时

与预加载一起的是可配置的超时,它确定用户必须悬停在链接上多长时间才能触发基于意图的预加载。默认超时是 50 毫秒,但您可以通过向 Link 组件传递 preloadTimeout 属性来更改此设置,该属性包含您希望等待的毫秒数:

tsx
const link = (
  <Link to="/blog/post/$postId" preload="intent" preloadTimeout={100}>
    Blog Post
  </Link>
)
const link = (
  <Link to="/blog/post/$postId" preload="intent" preloadTimeout={100}>
    Blog Post
  </Link>
)

useNavigate

⚠️ 由于 Link 组件围绕 href、cmd/ctrl + 点击能力和活动/非活动功能的内置便利性,建议对用户可以交互的任何内容(例如链接、按钮)使用 Link 组件而不是 useNavigate。但是,在某些情况下,useNavigate 对于处理副作用导航(例如导致导航的成功异步操作)是必要的。

useNavigate 钩子返回一个可以调用以进行命令式导航的 navigate 函数。这是从副作用(例如成功的异步操作)导航到路由的好方法。这是一个例子:

tsx
function Component() {
  const navigate = useNavigate({ from: '/posts/$postId' })

  const handleSubmit = async (e: FrameworkFormEvent) => {
    e.preventDefault()

    const response = await fetch('/posts', {
      method: 'POST',
      body: JSON.stringify({ title: 'My First Post' }),
    })

    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }
}
function Component() {
  const navigate = useNavigate({ from: '/posts/$postId' })

  const handleSubmit = async (e: FrameworkFormEvent) => {
    e.preventDefault()

    const response = await fetch('/posts', {
      method: 'POST',
      body: JSON.stringify({ title: 'My First Post' }),
    })

    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }
}

🧠 如上所示,您可以传递 from 选项来指定在钩子调用中要从中导航的路由。虽然每次调用时也可以在结果 navigate 函数中传递此选项,但建议在这里传递它以减少潜在错误并且不用输入那么多!

useNavigate 返回的 navigate 函数接受 NavigateOptions 接口

有时,您可能发现自己需要在组件挂载时立即导航。您的第一直觉可能是使用 useNavigate 和立即副作用(例如 useEffect),但这是不必要的。相反,您可以渲染 Navigate 组件来实现相同的结果:

tsx
function Component() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}
function Component() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}

Navigate 组件视为在组件挂载时立即导航到路由的方式。这是处理仅客户端重定向的好方法。它_绝对不是_在服务器上负责任地处理服务器感知重定向的替代品。

router.navigate

router.navigate 方法与 useNavigate 返回的 navigate 函数相同,并接受相同的 NavigateOptions 接口。与 useNavigate 钩子不同,它在您的 router 实例可用的任何地方都可用,因此是从应用程序中的任何地方(包括框架外部)进行命令式导航的好方法。

useMatchRoute<MatchRoute>

useMatchRoute 钩子和 <MatchRoute> 组件是同一个东西,但钩子更灵活一些。它们都接受标准导航 ToOptions 接口作为选项或属性,如果该路由当前匹配则返回 true/false。它还有一个方便的 pending 选项,如果路由当前处于待处理状态(例如路由当前正在转换到该路由),则返回 true。这对于显示用户导航位置周围的乐观 UI 非常有用:

tsx
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          <Spinner />
        </MatchRoute>
      </Link>
    </div>
  )
}
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          <Spinner />
        </MatchRoute>
      </Link>
    </div>
  )
}

组件版本 <MatchRoute> 也可以与函数作为子组件一起使用,以在路由匹配时渲染某些内容:

tsx
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          {(match) => {
            return <Spinner show={match} />
          }}
        </MatchRoute>
      </Link>
    </div>
  )
}
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          {(match) => {
            return <Spinner show={match} />
          }}
        </MatchRoute>
      </Link>
    </div>
  )
}

钩子版本 useMatchRoute 返回一个可以编程调用以检查路由是否匹配的函数:

tsx
function Component() {
  const matchRoute = useMatchRoute()

  useEffect(() => {
    if (matchRoute({ to: '/users', pending: true })) {
      console.info('The /users route is matched and pending')
    }
  })

  return (
    <div>
      <Link to="/users">Users</Link>
    </div>
  )
}
function Component() {
  const matchRoute = useMatchRoute()

  useEffect(() => {
    if (matchRoute({ to: '/users', pending: true })) {
      console.info('The /users route is matched and pending')
    }
  })

  return (
    <div>
      <Link to="/users">Users</Link>
    </div>
  )
}

呼!这是很多导航内容!话虽如此,希望您现在对在应用程序中导航感觉很好。让我们继续!