页面间的导航:`<Link>` 组件和 `useRouter`

页面间的导航:<Link> 组件和 useRouter

作者:码力无边


在上一篇文章中,我们掌握了如何通过文件系统创建静态、嵌套和动态的页面。现在,我们的应用有了多个独立的"房间",但缺少连接它们的"走廊"。今天,我们将学习如何在这些页面之间建立流畅、高效的导航,这也是提升用户体验的关键一步。

为什么不能直接用 <a> 标签?

在传统的 HTML 中,我们使用 <a> 标签进行页面跳转:

html 复制代码
<a href="/about">关于我们</a>

你完全可以在 Next.js 中这样做,但你会立刻发现一个问题:每次点击链接,浏览器都会进行一次完整的页面刷新。状态会丢失,整个应用会重新加载,这完全违背了我们使用 React 这样的单页应用(SPA)框架的初衷。我们追求的是那种无需刷新、如丝般顺滑的页面切换体验。

为了实现这种客户端导航 (Client-Side Navigation),Next.js 提供了两个核心工具:

  1. <Link> 组件:用于声明式导航,是绝大多数情况下的首选。
  2. useRouter 钩子:用于编程式(或命令式)导航,在特定逻辑触发后执行跳转。

1. 声明式导航:<Link> 组件的核心用法

<Link> 组件是 Next.js 导航的基石。它在背后做了大量优化工作,以确保导航既快速又高效。

基础用法

要使用它,首先从 next/link 中导入,然后用它包裹一个 <a> 标签。

tsx 复制代码
// pages/index.tsx
import Link from 'next/link';

export default function HomePage() {
  return (
    <div>
      <h1>欢迎来到首页</h1>
      <nav>
        <Link href="/about">
          <a>关于我们</a>
        </Link>
        <br />
        <Link href="/contact">
          <a>联系我们</a>
        </Link>
      </nav>
    </div>
  );
}

注意 :在旧版本的 Next.js 中,<Link> 必须包裹一个 <a> 标签。从 Next.js 13 的 app 目录开始,你可以不再需要手动添加 <a> 标签。但在我们目前学习的 pages 目录中,包裹 <a> 标签仍然是推荐的最佳实践。

现在点击链接,你会发现页面内容瞬间切换,而浏览器标签页并没有出现刷新的加载动画。这就是客户端导航的魔力!

导航到动态路由

链接到动态页面也同样简单。假设我们要链接到一篇 slug 为 hello-world 的博客文章,其页面文件是 pages/posts/[slug].tsx

你可以使用模板字符串:

tsx 复制代码
<Link href="/posts/hello-world">
  <a>阅读第一篇文章</a>
</Link>

或者,为了更清晰和更强的类型支持,可以使用一个 URL 对象:

tsx 复制代码
<Link
  href={{
    pathname: '/posts/[slug]',
    query: { slug: 'hello-world' },
  }}
>
  <a>阅读第一篇文章</a>
</Link>

当 URL 结构变得复杂时,使用对象形式 href 会让代码更具可读性和可维护性。

2. 编程式导航:useRouter 钩子

有时候,我们不能简单地让用户点击一个链接来跳转。例如:

  • 用户提交表单后,需要跳转到成功页面。
  • 用户登录成功后,需要跳转到个人中心。
  • 基于某些复杂的业务逻辑判断后,需要将用户重定向。

在这些场景下,我们需要在 JavaScript 代码中主动触发导航。useRouter 钩子就是为此而生。

基本跳转:router.push()

useRouter 钩子返回一个路由器(router)对象,该对象包含了路由信息和一些方法,其中最常用的就是 push 方法。

tsx 复制代码
// components/LoginForm.tsx
import { useRouter } from 'next/router';

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

  const handleSubmit = (event) => {
    event.preventDefault();
    // ... 假设这里处理了登录逻辑 ...
    const isLoginSuccess = true; // 模拟登录成功

    if (isLoginSuccess) {
      // 登录成功,跳转到仪表盘页面
      router.push('/dashboard');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... 表单输入框 ... */}
      <button type="submit">登录</button>
    </form>
  );
}

获取路由参数

useRouter 不仅仅能用于导航。在上一篇文章中,我们用它来获取动态路由的参数。这里再回顾一下,router.query 对象包含了 URL 中的所有查询参数和动态路由参数。

tsx 复制代码
// pages/posts/[slug].tsx
import { useRouter } from 'next/router';

function PostPage() {
  const router = useRouter();
  const { slug } = router.query; // 获取动态参数 slug

  return <h1>文章:{slug}</h1>;
}

3. 干货满满:解锁 <Link>useRouter 的高级功能

掌握了基础用法,我们再来深入挖掘一些能显著提升应用性能和用户体验的高级特性。

a) 预取(Prefetching):让导航快如闪电

这是 <Link> 组件最神奇的功能之一。默认情况下,当一个 <Link> 组件出现在用户的视口(viewport)中时,Next.js 会在后台自动预取链接指向页面的代码

这意味着,当用户真正点击链接时,所需的所有资源都已准备就绪,页面切换几乎是瞬时的。这个小小的优化对用户体验的提升是巨大的。

在大多数情况下,你不需要做任何事,这个功能是自动开启的。如果你想禁用它(例如在一个有成百上千个链接的页面上),可以设置 prefetch={false}

tsx 复制代码
<Link href="/very-large-page" prefetch={false}>
  <a>一个非常大的页面(不预取)</a>
</Link>

b) 替换历史记录:replace

默认情况下,router.push()<Link> 都会在浏览器的历史记录中添加一条新记录,这样用户可以点击"后退"按钮返回。

但在某些场景下,我们不希望用户能返回。例如,从登录页跳转到主页后,我们不希望用户能通过后退回到登录页。这时,可以使用 replace 选项。

使用 <Link>:

tsx 复制代码
<Link href="/dashboard" replace>
  <a>登录</a>
</Link>

使用 useRouter:

javascript 复制代码
router.replace('/dashboard');

这会替换掉当前的历史记录,而不是新增一条。

c) 控制滚动行为:scroll

默认情况下,每次导航后,Next.js 都会将页面滚动到顶部。这符合大多数用户的预期。但如果你不希望发生滚动(例如,在一个带有标签页的页面内切换内容),可以设置 scroll={false}

tsx 复制代码
<Link href="/?tab=settings" scroll={false}>
  <a>切换到设置标签</a>
</Link>

当你点击这个链接时,URL 会改变,页面内容也会更新,但滚动条的位置会保持不变。

最佳实践与总结

  1. 内部导航用 <Link> :只要是你的应用内部页面之间的跳转,并且是由用户直接点击触发的,就优先使用 <Link> 组件,以享受预取等性能优化。

  2. 外部链接用 <a> :如果要链接到其他网站,请直接使用标准的 <a> 标签。

  3. 编程式跳转用 useRouter :在事件处理函数、副作用(useEffect)等需要用代码控制导航的场景下,使用 useRouter

  4. 样式化 <Link><Link> 组件本身不接受 className 等样式属性,因为它不渲染任何可见的 DOM 元素。你应该将样式应用在它内部的 <a> 标签或其他组件上。

tsx 复制代码
// 使用 CSS Modules
import styles from './MyLink.module.css';

<Link href="/about">
  <a className={styles.link}>关于</a>
</Link>

今天,我们不仅学会了如何在 Next.js 页面间创建基本的导航,还深入了解了背后的性能优化机制和高级用法。一个好的导航系统是应用的骨架,而 Next.js 已经为我们提供了坚固且高效的工具。

现在我们的应用有了结构和导航,是时候让它变得好看了。在下一篇文章中,我们将探讨在 Next.js 中样式化的各种方法,包括 CSS 模块、Tailwind CSS 和全局样式。敬请期待!

相关推荐
代码搬运媛4 小时前
Jest 测试框架详解与实现指南
前端
counterxing5 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq5 小时前
windows下nginx的安装
linux·服务器·前端
之歆5 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜6 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108086 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
kyriewen7 小时前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor
humcomm8 小时前
元框架的工作原理详解
前端·前端框架
canonical_entropy8 小时前
Attractor Before Harness: AI 大规模开发的方法论
前端·aigc·ai编程
zhangxingchao9 小时前
多 Agent 架构到底怎么选?从 Claude Agent Teams、Cognition/Devin 到工程落地原则
前端·人工智能·后端