页面间的导航:`<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 和全局样式。敬请期待!

相关推荐
whysqwhw3 小时前
Kuikly 扩展原生 API 的完整流程
前端
whysqwhw3 小时前
Hippy 跨平台框架扩展原生自定义组件
前端
faimi4 小时前
🚀程序员必收藏!最全Git命令手册:解决90%团队协作难题
前端·gitlab
coooliang4 小时前
【鸿蒙 NEXT】V1迁移V2状态管理
java·前端·harmonyos
程序员码歌5 小时前
零代码AI编程实战-热搜从0到1技术方案
前端·ai编程·cursor
kk不中嘞5 小时前
浅谈前端框架
前端·vue.js·react.js·前端框架
服务端技术栈5 小时前
历时 1 个多月,我的第一个微信小程序「图片转 Excel」终于上线了!
前端·后端·微信小程序
一个很老的小萌新5 小时前
json 解析 [{“id“:1,“name“:“apple“},{“id“:2,“name“:“banana“}]
java·前端·json
yanlele6 小时前
前端面试第 78 期 - 2025.09.07 更新 Nginx 专题面试总结(12 道题)
前端·javascript·面试