跳至内容

链接和导航

在 Next.js 中,有四种方法可以在路由之间导航。

本页面将介绍如何使用这些选项,并深入探讨导航的工作原理。

<Link> 是一个内置组件,它扩展了 HTML <a> 标签以提供 预取 和路由之间的客户端导航。它是 Next.js 中在路由之间导航的主要和推荐方式。

您可以通过从 next/link 导入它并向组件传递 href 属性来使用它。

app/page.tsx
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

您可以向 <Link> 传递其他可选属性。有关更多信息,请参阅 API 参考

useRouter() 钩子

useRouter 钩子允许您以编程方式从 客户端组件 中更改路由。

app/page.js
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

有关 useRouter 方法的完整列表,请参阅 API 参考

建议:除非您有使用 useRouter 的特定需求,否则请使用 <Link> 组件在路由之间导航。

redirect 函数

对于 服务器组件,请改用 redirect 函数。

app/team/[id]/page.tsx
import { redirect } from 'next/navigation'
 
async function fetchTeam(id: string) {
  const res = await fetch('https://...')
  if (!res.ok) return undefined
  return res.json()
}
 
export default async function Profile({ params }: { params: { id: string } }) {
  const team = await fetchTeam(params.id)
  if (!team) {
    redirect('/login')
  }
 
  // ...
}

需要了解:

  • redirect 默认返回 307 (临时重定向) 状态代码。在服务器操作中使用时,它返回 303 (查看其他),这通常用于在 POST 请求的结果中重定向到成功页面。
  • redirect 在内部抛出错误,因此应在 try/catch 块之外调用它。
  • redirect 可以在渲染过程中在客户端组件中调用,但不能在事件处理程序中调用。您可以改用 useRouter 钩子
  • redirect 还接受绝对 URL,可用于重定向到外部链接。
  • 如果您希望在渲染过程之前重定向,请使用 next.config.js中间件

有关更多信息,请参阅 redirect API 参考

使用原生 History API

Next.js 允许您使用原生 window.history.pushStatewindow.history.replaceState 方法来更新浏览器的历史堆栈,而无需重新加载页面。

pushStatereplaceState 调用集成到 Next.js 路由器中,允许您与 usePathnameuseSearchParams 同步。

window.history.pushState

使用它向浏览器的历史堆栈添加新条目。用户可以导航回上一个状态。例如,对产品列表进行排序

'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function SortProducts() {
  const searchParams = useSearchParams()
 
  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString())
    params.set('sort', sortOrder)
    window.history.pushState(null, '', `?${params.toString()}`)
  }
 
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  )
}

window.history.replaceState

使用它替换浏览器历史堆栈中的当前条目。用户无法导航回上一个状态。例如,切换应用程序的语言环境

'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale: string) {
    // e.g. '/en/about' or '/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }
 
  return (
    <>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('fr')}>French</button>
    </>
  )
}

路由和导航的工作原理

App Router 使用混合方法进行路由和导航。在服务器端,您的应用程序代码会根据路由段自动 代码分割。在客户端,Next.js 会 预取缓存 路由段。这意味着,当用户导航到新路由时,浏览器不会重新加载页面,并且只有发生变化的路由段会重新渲染 - 从而改善导航体验和性能。

1. 代码分割

代码分割允许您将应用程序代码拆分为更小的包,以便浏览器下载和执行。这减少了每次请求传输的数据量和执行时间,从而提高了性能。

服务器组件 允许根据路由段自动对应用程序代码进行代码分割。这意味着只有当前路由所需的代码才会在导航时加载。

2. 预取

预取是在用户访问路由之前在后台预加载路由的一种方式。

在 Next.js 中,有两种方式可以预取路由

  • <Link> 组件:当路由在用户的视口中变得可见时,会自动对其进行预取。预取发生在页面首次加载时或通过滚动进入视图时。
  • router.prefetch()useRouter 钩子可以用来以编程方式预取路由。

<Link> 的默认预取行为(即当 prefetch 属性未指定或设置为 null 时)取决于您对 loading.js 的使用。只有共享布局,以及在渲染的组件“树”中一直到第一个 loading.js 文件,才会被预取并缓存 30s。这减少了获取整个动态路由的成本,并且意味着您可以显示 即时加载状态 以向用户提供更好的视觉反馈。

您可以通过将 prefetch 属性设置为 false 来禁用预取。或者,您可以通过将 prefetch 属性设置为 true 来预取超出加载边界的完整页面数据。

有关更多信息,请参阅 <Link> API 参考

需要了解:

  • 预取在开发环境中未启用,仅在生产环境中启用。

3. 缓存

Next.js 具有一个名为 路由器缓存内存客户端缓存。当用户在应用程序中导航时,预取 的路由段和已访问路由的 React 服务器组件有效负载将存储在缓存中。

这意味着在导航时,尽可能地重用缓存,而不是向服务器发出新请求 - 通过减少请求数和传输的数据量来提高性能。

了解有关 路由器缓存 的工作原理以及如何配置它的更多信息。

4. 部分渲染

部分渲染意味着只有在导航时发生更改的路由段才会在客户端重新渲染,并且任何共享段都将保留。

例如,在两个兄弟路由 /dashboard/settings/dashboard/analytics 之间导航时,settings 页面将被卸载,analytics 页面将使用新状态挂载,并且共享的 dashboard 布局将被保留。此行为也存在于同一动态段上的两个路由之间,例如 /blog/[slug]/page 以及从 /blog/first 导航到 /blog/second

How partial rendering works

如果没有部分渲染,每次导航都将导致整个页面在客户端重新渲染。仅渲染发生更改的段减少了传输的数据量和执行时间,从而提高了性能。

5. 软导航

浏览器在页面之间导航时执行“硬导航”。Next.js App Router 能够在页面之间实现“软导航”,确保只有发生更改的路由段重新渲染(部分渲染)。这使得可以在导航期间保留客户端 React 状态。

6. 前进和后退导航

默认情况下,Next.js 将为前进和后退导航保留滚动位置,并在 路由器缓存 中重用路由段。

7. pages/app/ 之间的路由

在从 pages/ 逐步迁移到 app/ 时,Next.js 路由器将自动处理这两个目录之间的硬导航。为了检测从 pages/app/ 的转换,有一个客户端路由过滤器利用 app 路由的概率检查,这偶尔会导致误报。默认情况下,此类事件应该非常少见,因为我们将误报可能性配置为 0.01%。此可能性可以通过 next.config.js 中的 experimental.clientRouterFilterAllowedRate 选项进行自定义。需要注意的是,降低误报率会增加客户端包中生成的过滤器的尺寸。

或者,如果您希望完全禁用此处理并手动管理 pages/app/ 之间的路由,则可以在 next.config.js 中将 experimental.clientRouterFilter 设置为 false。禁用此功能后,默认情况下,pages 中与 app 路由重叠的任何动态路由都无法正确导航到。