Skip to content

路由跳转

前言

上篇我们介绍了如何定义路由,本篇我们讲讲如何在 Next.js 中实现链接和导航。

所谓“导航”,指的是使用 JavaScript 进行页面切换,通常会比浏览器默认的重新加载更快,因为在导航的时候,只会更新必要的组件,而不会重新加载整个页面。

在 Next.js 中,有 4 种方式可以实现路由导航:

  • 使用 <Link> 组件

  • 使用 useRouter Hook(客户端组件)

  • 使用 redirect 函数(服务端组件)

  • 使用浏览器原生 History API

Next.js 的<Link>组件是一个拓展了原生 HTML <a> 标签的内置组件,用来实现预获取数据和客户端路由导航。这是 Next.js 中路由导航的主要和推荐方式。

基础使用

  • 基本的方式如下:
ts
import Link from "next/link";

export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>;
}
  • 支持动态渲染
ts
import Link from "next/link";

export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  );
}
  • 获取当前路径名

如果需要对当前链接进行判断,你可以使用 usePathname()

它会读取当前 URL 的路径名(pathname)。示例代码如下:

ts
"use client"; //必须要加 usePathname()只能在客户端使用

import { usePathname } from "next/navigation";
import Link from "next/link";

export function Navigation({ navLinks }) {
  const pathname = usePathname(); // 获取到当前路径 比如:/dashboard

  return (
    <>
      {navLinks.map((link) => {
        const isActive = pathname === link.href;
        return (
          <Link
            className={isActive ? "text-blue" : "text-black"}
            href={link.href}
            key={link.name}
          >
            {link.name}
          </Link>
        );
      })}
    </>
  );
}
  • 跳转行为设置

App Router 的默认行为是滚动到新路由的顶部,或者在前进后退导航时维持之前的滚动距离。

如果你想要禁用这个行为,你可以给 <Link> 组件传递一个 scroll={false}属性,或者在使用 router.pushrouter.replace 的时候,设置 scroll: false

开始标签跳转

ts
// next/link
<Link href="/dashboard" scroll={false}>
  Dashboard
</Link>

开始方法跳转

ts
// useRouter
"use client";
import { useRouter } from "next/navigation";

const router = useRouter();

router.push("/dashboard", { scroll: false });

useRouter() hook

方法跳转

ts
"use client";

import { useRouter } from "next/navigation";

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

  return (
    <button type="button" onClick={() => router.push("/dashboard")}>
      Dashboard
    </button>
  );
}

注意使用该 hook 需要在客户端组件中。(顶层的 'use client' 就是声明这是客户端组件)

redirect() 函数

客户端组件使用 useRouter hook,服务端组件则可以直接使用 redirect 函数,这也是 Next.js 提供的 API,使用示例代码如下:

ts
import { redirect } from "next/navigation";

async function fetchTeam(id) {
  const res = await fetch("https://...");
  if (!res.ok) return undefined;
  return res.json();
}

export default async function Profile({ params }) {
  const team = await fetchTeam(params.id);
  if (!team) {
    redirect("/login");
  }

  // ...
}

History API

  • 比如用 pushState 对列表进行排序:
ts
"use client";

import { useSearchParams } from "next/navigation";

export default function SortProducts() {
  const searchParams = useSearchParams();

  function updateSorting(sortOrder) {
    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>
    </>
  );
}
  • replaceState 会替换浏览器历史堆栈的当前条目,替换后用户无法后退,比如切换应用的地域设置(国际化):
ts
"use client";

import { usePathname } from "next/navigation";

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

  function switchLocale(locale) {
    // 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>
    </>
  );
}