路由链接与导航
在 Next.js 中有四种方式实现路由导航:
- 使用
<Link>
组件 - 使用
useRouter
钩子 (客户端组件) - 使用
redirect
函数 (服务端组件) - 使用原生 History API
本文将介绍如何使用这些方法,并深入探讨导航的工作原理。
<Link>
组件
<Link>
是内置组件,它扩展了 HTML <a>
标签的功能,提供预加载和客户端路由导航。这是 Next.js 中推荐的主要路由导航方式。
你可以从 next/link
导入该组件,并通过 href
属性指定目标路由:
<Link>
还支持其他可选属性,详见 API 参考文档。
使用示例
链接到动态路由段
当链接到动态路由段时,可以使用模板字符串和插值生成链接列表。例如生成博客文章列表:
检测当前活动链接
可以使用 usePathname()
判断链接是否处于活动状态。例如,为活动链接添加特定类名:
滚动到指定 id
Next.js 应用路由器的默认行为是:导航到新路由时滚动到页面顶部,或在前进/后退导航时保持原有滚动位置。
如需滚动到特定 id
元素,可以在 URL 后添加 #
哈希链接,或直接将哈希链接作为 href
属性值。这是因为 <Link>
最终会渲染为 <a>
元素。
须知:
- 如果目标页面在视窗中不可见,Next.js 会在导航时自动滚动到页面顶部。
禁用滚动恢复
Next.js 应用路由器的默认行为是:导航到新路由时滚动到页面顶部,或在前进/后退导航时保持原有滚动位置。如需禁用此行为,可以向 <Link>
组件传递 scroll={false}
,或向 router.push()
/router.replace()
传递 scroll: false
。
useRouter()
钩子
useRouter
钩子允许你在客户端组件中以编程方式更改路由。
完整的 useRouter
方法列表,请参阅 API 参考文档。
推荐:除非有特殊需求,否则优先使用
<Link>
组件进行路由导航。
redirect
函数
对于服务端组件,请使用 redirect
函数。
须知:
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
用于向浏览器历史记录栈添加新条目。用户可以导航回之前的状态。例如实现商品排序功能:
window.history.replaceState
用于替换浏览器历史记录栈中的当前条目。用户无法导航回之前的状态。例如实现语言切换功能:
路由与导航工作原理
应用路由器采用混合方式实现路由和导航。在服务端,你的应用代码会按路由段自动进行代码分割。在客户端,Next.js 会预加载并缓存路由段。这意味着当用户导航到新路由时,浏览器不会重新加载页面,只有发生变化的路由段会重新渲染——从而提升导航体验和性能。
1. 代码分割
代码分割允许将应用代码拆分为更小的包,由浏览器下载和执行。这减少了每次请求传输的数据量和执行时间,从而提升性能。
服务端组件使得应用代码能按路由段自动分割。这意味着导航时只会加载当前路由所需的代码。
2. 预加载
预加载是一种在用户访问前提前加载路由的机制。
Next.js 中有两种预加载方式:
<Link>
组件:当链接出现在用户视窗中时会自动预加载。预加载发生在页面首次加载或通过滚动进入视窗时。router.prefetch()
:可通过useRouter
钩子以编程方式预加载路由。
<Link>
的默认预加载行为(即不指定 prefetch
属性或设为 null
时)会根据 loading.js
的使用情况有所不同。只会预加载共享布局,直到第一个 loading.js
文件为止的组件树,并缓存 30 秒。这降低了获取整个动态路由的成本,同时可以显示即时加载状态以提供更好的视觉反馈。
通过将 prefetch
属性设为 false
可禁用预加载。或者,通过设为 true
可以预加载超出加载边界的完整页面数据。
更多信息请参阅 <Link>
API 参考文档。
须知:
- 预加载仅在生产环境启用,开发环境不会生效。
3. 缓存
Next.js 有一个称为路由缓存的内存客户端缓存。当用户在应用中导航时,预加载的路由段和已访问路由的 React 服务端组件负载会被存储在缓存中。
这意味着导航时会尽可能复用缓存,而不是向服务器发起新请求——通过减少请求次数和数据传输量来提升性能。
详细了解路由缓存的工作原理及配置方法。
4. 部分渲染
部分渲染意味着导航时只有发生变化的路由段会在客户端重新渲染,共享的布局会保持不变。
例如,在兄弟路由 /dashboard/settings
和 /dashboard/analytics
之间导航时,只会渲染 settings
和 analytics
页面,共享的 dashboard
布局会保留。

如果没有部分渲染,每次导航都会导致整个页面在客户端重新渲染。只渲染变化的部分减少了数据传输量和执行时间,从而提升性能。
5. 软导航
浏览器在页面间导航时会执行"硬导航"。Next.js 应用路由器实现了页面间的"软导航",确保只有变化的路由段会重新渲染(部分渲染)。这使得客户端的 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。禁用此功能后,任何与应用路由重叠的 pages 动态路由默认将无法正确导航。