链接和导航
在 Next.js 中,有四种方法可以在路由之间导航
- 使用
<Link>
组件 - 使用
useRouter
钩子(客户端组件) - 使用
redirect
函数(服务器组件) - 使用原生 History API
本页将介绍如何使用这些选项,并深入探讨导航的工作原理。
<Link>
组件
<Link>
是一个内置组件,它扩展了 HTML <a>
标签,以提供 预取和路由之间的客户端导航。它是 Next.js 中路由之间导航的主要和推荐方式。
你可以通过从 next/link
导入它来使用它,并将 href
属性传递给组件
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">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')}>
Dashboard
</button>
)
}
有关 useRouter
方法的完整列表,请参阅 API 参考。
建议: 除非你有使用
useRouter
的特定要求,否则请使用<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).id
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.pushState
和 window.history.replaceState
方法来更新浏览器的历史堆栈,而无需重新加载页面。
pushState
和 replaceState
调用集成到 Next.js 路由器中,允许你与 usePathname
和 useSearchParams
同步。
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
文件,才会被预取和缓存 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
。


如果没有部分渲染,每次导航都会导致整个页面在客户端重新渲染。仅渲染更改的段可以减少数据传输量和执行时间,从而提高性能。
5. 软导航
浏览器在页面之间导航时执行“硬导航”。Next.js App Router 启用页面之间的“软导航”,确保仅重新渲染已更改的路由段(部分渲染)。这使得客户端 React 状态在导航期间得以保留。
6. 后退和前进导航
默认情况下,Next.js 将保持后退和前进导航的滚动位置,并在 路由器缓存中重用路由段。
7. 在 pages/
和 app/
之间路由
当从 pages/
增量迁移到 app/
时,Next.js 路由器将自动处理两者之间的硬导航。为了检测从 pages/
到 app/
的转换,有一个客户端路由器过滤器,它利用应用程序路由的概率检查,这偶尔可能导致误报。默认情况下,这种情况应该非常罕见,因为我们将误报可能性配置为 0.01%。可以通过 next.config.js
中的 experimental.clientRouterFilterAllowedRate
选项自定义此可能性。重要的是要注意,降低误报率将增加客户端捆绑包中生成的过滤器的大小。
或者,如果你希望完全禁用此处理并手动管理 pages/
和 app/
之间的路由,则可以将 next.config.js
中的 experimental.clientRouterFilter
设置为 false。禁用此功能后,默认情况下,页面中与应用程序路由重叠的任何动态路由都将无法正确导航。
这有帮助吗?