链接与导航

Next.js 的路由器允许您在页面之间进行客户端路由跳转,类似于单页应用的行为。

为此,Next.js 提供了一个名为 Link 的 React 组件来实现客户端路由跳转。

import Link from 'next/link'

function Home() {
  return (
    <ul>
      <li>
        <Link href="/">首页</Link>
      </li>
      <li>
        <Link href="/about">关于我们</Link>
      </li>
      <li>
        <Link href="/blog/hello-world">博客文章</Link>
      </li>
    </ul>
  )
}

export default Home

上面的示例使用了多个链接。每个链接都将路径 (href) 映射到一个已知页面:

  • /pages/index.js
  • /aboutpages/about.js
  • /blog/hello-worldpages/blog/[slug].js

视口内的任何 <Link />(初始状态或通过滚动进入)都会默认预获取页面(包括对应数据),适用于使用 静态生成 (Static Generation) 的页面。对于 服务端渲染 (server-rendered) 的路由,对应数据仅在点击 <Link /> 时获取。

链接到动态路径

您还可以使用插值来创建路径,这对于 动态路由段 (dynamic route segments) 非常有用。例如,显示作为 prop 传递给组件的文章列表:

import Link from 'next/link'

function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${encodeURIComponent(post.slug)}`}>
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Posts

示例中使用了 encodeURIComponent 来保持路径的 utf-8 兼容性。

或者,使用 URL 对象:

import Link from 'next/link'

function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link
            href={{
              pathname: '/blog/[slug]',
              query: { slug: post.slug },
            }}
          >
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Posts

现在,我们不再使用插值创建路径,而是在 href 中使用 URL 对象,其中:

  • pathnamepages 目录中的页面名称。本例中为 /blog/[slug]
  • query 是包含动态段的对象。本例中为 slug

注入路由

示例

要在 React 组件中访问 router 对象,您可以使用 useRouterwithRouter

通常我们推荐使用 useRouter

命令式路由

next/link 应该能满足您的大部分路由需求,但您也可以在不使用它的情况下进行客户端导航,请参阅 next/router 文档

以下示例展示了如何使用 useRouter 进行基本页面导航:

import { useRouter } from 'next/router'

export default function ReadMore() {
  const router = useRouter()

  return (
    <button onClick={() => router.push('/about')}>
      点击此处阅读更多
    </button>
  )
}

浅层路由

示例

浅层路由允许您更改 URL 而不再次运行数据获取方法,包括 getServerSidePropsgetStaticPropsgetInitialProps

您将通过 router 对象(由 useRouterwithRouter 添加)接收更新的 pathnamequery,而不会丢失状态。

要启用浅层路由,请将 shallow 选项设置为 true。参考以下示例:

import { useEffect } from 'react'
import { useRouter } from 'next/router'

// 当前 URL 是 '/'
function Page() {
  const router = useRouter()

  useEffect(() => {
    // 总是在首次渲染后进行导航
    router.push('/?counter=10', undefined, { shallow: true })
  }, [])

  useEffect(() => {
    // counter 发生了变化!
  }, [router.query.counter])
}

export default Page

URL 将更新为 /?counter=10 且页面不会被替换,仅路由状态发生变化。

您也可以通过 componentDidUpdate 监听 URL 变化,如下所示:

componentDidUpdate(prevProps) {
  const { pathname, query } = this.props.router
  // 验证 props 是否已更改以避免无限循环
  if (query.counter !== prevProps.router.query.counter) {
    // 根据新查询获取数据
  }
}

注意事项

浅层路由 适用于当前页面的 URL 更改。例如,假设我们有另一个页面 pages/about.js,您执行以下操作:

router.push('/?counter=10', '/about?counter=10', { shallow: true })

由于这是一个新页面,它将卸载当前页面,加载新页面并等待数据获取,即使我们要求进行浅层路由。

当浅层路由与中间件一起使用时,它将不再像之前没有中间件时那样确保新页面与当前页面匹配。这是由于中间件能够动态重写,并且无法在不进行数据获取的情况下在客户端验证(浅层路由会跳过数据获取),因此必须始终将浅层路由更改视为浅层操作。

On this page