路由导航

2026年9月6日

同站跳转不要用原生 <a href> 硬刷新。框架提供了预取、保滚动、替换历史这几件事,按场景选 API。

常用四条:

  1. <Link>:声明式,菜单、列表最合适
  2. useRouter:点击、权限判断后在客户端命令式跳
  3. redirect / permanentRedirect:服务端(或 Server Action)里改去向
  4. History API:极少用,知道即可

next/link<a> 上加了预取和客户端切换:

import Link from "next/link";

export default function Nav() {
  return (
    <nav>
      <Link href="/about">关于</Link>
      <Link href={{ pathname: "/about", query: { name: "槿" } }}>带查询</Link>
      <Link href="/docs" prefetch>
        预取
      </Link>
      <Link href="/long-page" scroll={false}>
        不滚回顶部
      </Link>
      <Link href="/login" replace>
        替换历史
      </Link>
    </nav>
  );
}
tsx

列表里动态拼地址完全没问题:

{ids.map((id) => (
  <Link key={id} href={`/posts/${id}`}>
    文章 {id}
  </Link>
))}
tsx

视口里的 Link 默认会预取目标页。关闭:prefetch={false}replace 不会往历史栈再推一条,适合「登录成功盖掉登录页」。


useRouter

只能在客户端组件里用,从 next/navigation 引入(不要用旧的 next/router):

"use client";

import { useRouter } from "next/navigation";

export default function Toolbar() {
  const router = useRouter();

  return (
    <>
      <button onClick={() => router.push("/posts")}>去列表</button>
      <button onClick={() => router.replace("/login")}>替换到登录</button>
      <button onClick={() => router.back()}>后退</button>
      <button onClick={() => router.forward()}>前进</button>
      <button onClick={() => router.refresh()}>刷新服务端数据</button>
    </>
  );
}
tsx

refresh() 不会整页重载,而是重新跑当前路由的服务端组件。权限分支、表单成功后的跳转,用这套比一堆 Link 好写。


redirect

服务端组件或 Server Action 里改去向:

import { redirect } from "next/navigation";

export default async function Page() {
  const user = await getSession();
  if (!user) {
    redirect("/login");
  }
  return <p>已登录</p>;
}
tsx

redirect 发出的是 307 临时重定向。它会抛出一个框架认识的特殊错误,后面的代码不会继续跑,这是预期行为。


permanentRedirect

用法相同,状态码是 308 永久重定向。URL 搬家、旧路径作废时用它,方便搜索引擎更新索引。

import { permanentRedirect } from "next/navigation";

export default function OldDocs() {
  permanentRedirect("/docs/nextjs/01-getting-started");
}
tsx

两个函数都接收目标路径(相对或绝对)。在 Server Action / 客户端里还可以传 type: "push" | "replace" 控制历史栈;服务端组件里这个参数无效,一律按 replace 理解。

默认历史行为:

  • Server Action:默认 push
  • 其他场景:默认 replace

怎么选

场景用谁
导航、卡片、目录Link
按钮、校验后再跳useRouter
服务端发现没登录 / 数据不存在redirect
路径永久作废permanentRedirect

下一篇是动态段:[slug][...slug][[...slug]]


参考文档