跳到内容

链接与导航

Next.js 路由允许你在页面之间进行客户端路由转换,类似于单页应用程序。

提供了一个名为 `Link` 的 React 组件来执行此客户端路由转换。

import Link from 'next/link'
 
function Home() {
  return (
    <ul>
      <li>
        <Link href="/">Home</Link>
      </li>
      <li>
        <Link href="/about">About Us</Link>
      </li>
      <li>
        <Link href="/blog/hello-world">Blog Post</Link>
      </li>
    </ul>
  )
}
 
export default Home

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

  • `/` → `pages/index.js`
  • `/about` → `pages/about.js`
  • `/blog/hello-world` → `pages/blog/[slug].js`

视口中(最初或通过滚动)的任何 `` 都会默认被预取(包括相应的数据),用于使用静态生成的页面。对于服务器渲染路由,相应的数据仅在点击 `` 时获取。

链接到动态路径

你还可以使用插值来创建路径,这对于动态路由段非常有用。例如,要显示一个作为 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 对象,其中

  • `pathname` 是 `pages` 目录中页面的名称。在本例中是 `/blog/[slug]`。
  • `query` 是一个包含动态段的对象。在本例中是 `slug`。

注入路由

要在 React 组件中访问`router` 对象,可以使用`useRouter``withRouter`

通常,我们建议使用`useRouter`

命令式路由

`next/link` 应该能够满足你大部分的路由需求,但你也可以在没有它的情况下进行客户端导航,请查看`next/router` 的文档

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

import { useRouter } from 'next/router'
 
export default function ReadMore() {
  const router = useRouter()
 
  return (
    <button onClick={() => router.push('/about')}>
      Click here to read more
    </button>
  )
}

浅层路由

示例

浅层路由允许你更改 URL 而无需再次运行数据获取方法,这包括`getServerSideProps``getStaticProps``getInitialProps`

你将通过`router` 对象(由`useRouter``withRouter`添加)接收更新后的 `pathname` 和 `query`,而不会丢失状态。

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

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
// Current URL is '/'
function Page() {
  const router = useRouter()
 
  useEffect(() => {
    // Always do navigations after the first render
    router.push('/?counter=10', undefined, { shallow: true })
  }, [])
 
  useEffect(() => {
    // The counter changed!
  }, [router.query.counter])
}
 
export default Page

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

你还可以通过`componentDidUpdate` 监听 URL 变化,如下所示

componentDidUpdate(prevProps) {
  const { pathname, query } = this.props.router
  // verify props have changed to avoid an infinite loop
  if (query.counter !== prevProps.router.query.counter) {
    // fetch data based on the new query
  }
}

注意事项

浅层路由**仅**适用于当前页面的 URL 更改。例如,假设我们有另一个名为 `pages/about.js` 的页面,并且你运行此操作

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

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

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