链接和导航
Next.js 中有四种在路由之间进行导航的方式:
- 使用
<Link>
组件 - 使用
useRouter
hook (客户端组件) - 使用
redirect
函数 (服务器组件) - 使用原生 History API
本页将介绍如何使用这些选项,并深入探讨导航的工作原理。
<Link>
组件
<Link>
是一个内置组件,它扩展了 HTML 的 <a>
标签,提供 预获取 和客户端路由导航功能。这是 Next.js 中进行路由导航的主要推荐方式。
你可以通过从 next/link
导入并向组件传递 href
属性来使用它:
你还可以向 <Link>
传递其他可选属性。详见 API 参考。
useRouter()
hook
useRouter
hook 允许你在客户端组件中以编程方式更改路由。
关于 useRouter
方法的完整列表,请参见 API 参考。
建议: 除非你有使用
useRouter
的特定需求,否则请使用<Link>
组件进行路由导航。
redirect
函数
对于服务器组件,请使用 redirect
函数。
值得注意的是:
redirect
默认返回 307 (临时重定向) 状态码。当在服务器操作中使用时,它返回 303 (查看其他),这通常用于在 POST 请求后重定向到成功页面。redirect
内部会抛出错误,因此应该在try/catch
块之外调用。redirect
可以在渲染过程中在客户端组件中调用,但不能在事件处理程序中使用。你可以改用useRouter
hook。redirect
也接受绝对 URL,可用于重定向到外部链接。- 如果你想在渲染过程之前进行重定向,请使用
next.config.js
或 Middleware。
更多信息请参见 redirect
API 参考。
使用原生 History API
Next.js 允许你使用原生的 window.history.pushState
和 window.history.replaceState
方法来更新浏览器的历史记录堆栈,而无需重新加载页面。
pushState
和 replaceState
调用会集成到 Next.js 路由器中,允许你与 usePathname
和 useSearchParams
同步。
window.history.pushState
使用它向浏览器的历史记录堆栈添加新条目。用户可以导航回前一个状态。例如,对产品列表进行排序:
window.history.replaceState
使用它替换浏览器历史记录堆栈中的当前条目。用户无法导航回前一个状态。例如,切换应用程序的语言:
路由和导航的工作原理
App Router 采用混合方式进行路由和导航。在服务器端,你的应用程序代码会按路由段自动进行代码分割。而在客户端,Next.js 会预获取和缓存路由段。这意味着,当用户导航到新路由时,浏览器不会重新加载页面,只有变化的路由段会重新渲染——这改善了导航体验和性能。
1. 代码分割
代码分割允许你将应用程序代码分割成更小的包,供浏览器下载和执行。这减少了每个请求的数据传输量和执行时间,从而提高性能。
服务器组件允许你的应用程序代码按路由段自动进行代码分割。这意味着在导航时只加载当前路由所需的代码。
2. 预获取
预获取是一种在用户访问路由之前在后台预加载该路由的方式。
Next.js 中有两种预获取路由的方式:
<Link>
组件:当路由在用户视口中可见时,会自动预获取。预获取会在页面首次加载时或通过滚动进入视图时发生。router.prefetch()
:可以使用useRouter
hook 以编程方式预获取路由。
<Link>
的默认预获取行为(即当 prefetch
属性未指定或设置为 null
时)取决于你是否使用 loading.js
。只有共享布局,直到组件树中第一个 loading.js
文件的路径会被预获取和缓存 30s
。这减少了获取整个动态路由的成本,并且意味着你可以显示即时加载状态,为用户提供更好的视觉反馈。
你可以通过将 prefetch
属性设置为 false
来禁用预获取。另外,你可以通过将 prefetch
属性设置为 true
来预获取加载边界之外的完整页面数据。
详见 <Link>
API 参考。
值得注意的是:
- 预获取在开发环境中不启用,仅在生产环境中启用。
3. 缓存
Next.js 有一个名为路由器缓存的内存客户端缓存。当用户在应用程序中导航时,预获取的路由段和已访问路由的 React Server Component Payload 会存储在缓存中。
这意味着在导航时,尽可能复用缓存,而不是向服务器发出新请求——通过减少请求次数和数据传输量来提高性能。
了解更多关于路由器缓存的工作原理及其配置方法。
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/
的转换,这里有一个客户端路由器过滤器,它利用概率性检查 app 路由,这可能偶尔会导致误判。默认情况下,这种情况应该非常罕见,因为我们将误判概率配置为 0.01%。这个概率可以通过 next.config.js
中的 experimental.clientRouterFilterAllowedRate
选项进行自定义。需要注意的是,降低误判率会增加客户端包中生成的过滤器的大小。
另外,如果你希望完全禁用此处理并手动管理 pages/
和 app/
之间的路由,可以在 next.config.js
中将 experimental.clientRouterFilter
设置为 false。当此功能被禁用时,pages 中与 app 路由重叠的任何动态路由默认将无法正常导航。