Next.js 链接和导航教程:四种导航方式与路由原理

2025-03-21 18:51 更新

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

  • 使用 <Link> 组件
  • 使用 useRouter 钩子(客户端组件)
  • 使用 redirect 函数(服务器组件)
  • 使用原生 History API

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

<Link> 组件

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

你可以从 next/link 导入 <Link>,并通过传递 href 属性来使用它:

import Link from 'next/link'


export default function Page() {
  return <Link href="/dashboard">仪表盘</Link>
}

<Link> 还可以传递其他可选属性,更多信息请参考 API 文档。

useRouter() 钩子

useRouter 钩子允许你在客户端组件中通过代码更改路由。

'use client'


import { useRouter } from 'next/navigation'


export default function Page() {
  const router = useRouter()


  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      仪表盘
    </button>
  )
}

如需了解 useRouter 的所有方法,请参考 API 文档。

建议:除非有特定需求,否则建议使用 <Link& 组件进行路由导航。

redirect 函数

对于服务器组件,应使用 redirect 函数。

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: Promise<{ id: string }>
}) {
  const { id } = await params
  if (!id) {
    redirect('/login')
  }


  const team = await fetchTeam(id)
  if (!team) {
    redirect('/join')
  }


  // ...
}

需要注意的是

- 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')}>升序排序</button>
      <button onClick={() => updateSorting('desc')}>降序排序</button>
    </>
  )
}

window.history.replaceState

使用它替换浏览器历史记录栈中的当前条目。用户无法返回到之前的页面。例如,切换应用的语言:

'use client'


import { usePathname } from 'next/navigation'


export function LocaleSwitcher() {
  const pathname = usePathname()


  function switchLocale(locale: string) {
    // 例如:'/en/about' 或 '/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }


  return (
    <>
      <button onClick={() => switchLocale('en')}>英语</button>
      <button onClick={() => switchLocale('zh')}>中文</button>
    </>
  )
}

路由与导航的工作原理

应用路由器采用混合方式处理路由和导航。在服务器端,应用代码会根据路由段自动进行代码分割。在客户端,Next.js 会预取并缓存路由段。这意味着,当用户导航到新路由时,浏览器不会重新加载页面,只有更改的路由段会重新渲染,从而提升导航体验和性能。

1. 代码分割

代码分割允许将应用代码拆分为更小的bundle,供浏览器下载和执行。这减少了每个请求的数据传输量和执行时间,从而提升性能。

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

2. 预取

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

在 Next.js 中,路由通过以下两种方式预取:

  • <Link> 组件:当路由进入用户视口时,会自动预取。预取在页面首次加载或通过滚动进入视口时发生。
  • router.prefetch()useRouter 钩子可用于程序化预取路由。

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

你可以通过将 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

Next.js 链接和导航-部分渲染

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

5. 软导航

浏览器在页面之间导航时会执行“硬导航”。Next.js 应用路由器启用了页面之间的“软导航”,确保只有更改的路由段会重新渲染(部分渲染)。这使得客户端 React 状态在导航期间得以保留。

6. 返回和前进导航

默认情况下,Next.js 将维护返回和前进导航的滚动位置,并在路由器缓存中重复使用路由段。

7. 在 pages/app/ 之间路由

在从 pages/ 逐步迁移到 app/ 时,Next.js 路由器将自动处理两者之间的硬导航。为了检测从 pages/app/ 的过渡,有一个客户端路由器过滤器,它利用应用路由的概率检查,这偶尔会导致误报。默认情况下,这种误报的概率非常低(0.01%)。此概率可通过 next.config.js 中的 experimental.clientRouterFilterAllowedRate 选项进行调整。需要注意的是,降低误报率会增加客户端bundle中生成的过滤器的大小。

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

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号