跳到主要内容

链接与导航

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 />(初始时或通过滚动进入)默认都会被预取(包括相应的数据)。对于服务端渲染路由的相应数据,只有当 <Link /> 被点击时才会获取。

链接到动态路径

你也可以使用插值来创建路径,这对于动态路由段非常有用。例如,显示作为 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) {
// 基于新的 query 获取数据
}
}

注意事项

浅层路由适用于当前页面中的 URL 更改。例如,假设我们有另一个名为 pages/about.js 的页面,你运行:

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

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

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