Skip to main content

链接和导航

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

¥The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application.

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

¥A React component called Link is provided to do this client-side route transition.

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) 映射到已知页面:

¥The example above uses multiple links. Each one maps a path (href) to a known page:

  • /pages/index.js

  • /aboutpages/about.js

  • /blog/hello-worldpages/blog/[slug].js

对于使用 静态生成 的页面,默认情况下将预取视口中的任何 <Link />(初始或通过滚动)(包括相应的数据)。仅当点击 <Link /> 时才会获取 server-rendered 路由的相应数据。

¥Any <Link /> in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using Static Generation. The corresponding data for server-rendered routes is fetched only when the <Link /> is clicked.

链接到动态路径

¥Linking to dynamic paths

你还可以使用插值来创建路径,这对于 动态路由段 很方便。例如,要显示已作为 prop 传递给组件的帖子列表:

¥You can also use interpolation to create the path, which comes in handy for dynamic route segments. For example, to show a list of posts which have been passed to the component as a 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 兼容。

¥encodeURIComponent is used in the example to keep the path utf-8 compatible.

或者,使用 URL 对象:

¥Alternatively, using a URL Object:

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 对象,其中:

¥Now, instead of using interpolation to create the path, we use a URL object in href where:

  • pathnamepages 目录中页面的名称。在本例中为 /blog/[slug]

    ¥pathname is the name of the page in the pages directory. /blog/[slug] in this case.

  • query 是具有动态段的对象。在本例中为 slug

    ¥query is an object with the dynamic segment. slug in this case.

注入路由

¥Injecting the router

Examples

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

¥To access the router object in a React component you can use useRouter or withRouter.

一般来说我们建议使用 useRouter

¥In general we recommend using useRouter.

命令式路由

¥Imperative Routing

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

¥next/link should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the documentation for next/router.

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

¥The following example shows how to do basic page navigations with 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>
)
}

浅层路由

¥Shallow Routing

Examples

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

¥Shallow routing allows you to change the URL without running data fetching methods again, that includes getServerSideProps, getStaticProps, and getInitialProps.

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

¥You'll receive the updated pathname and the query via the router object (added by useRouter or withRouter), without losing state.

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

¥To enable shallow routing, set the shallow option to true. Consider the following example:

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,并且页面不会被替换,仅更改路由的状态。

¥The URL will get updated to /?counter=10 and the page won't get replaced, only the state of the route is changed.

你还可以通过 componentDidUpdate 观察 URL 变化,如下所示:

¥You can also watch for URL changes via componentDidUpdate as shown below:

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
}
}

注意事项

¥Caveats

浅层路由仅适用于当前页面的 URL 变化。例如,假设我们有另一个名为 pages/about.js 的页面,并且你运行以下命令:

¥Shallow routing only works for URL changes in the current page. For example, let's assume we have another page called pages/about.js, and you run this:

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

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

¥Since that's a new page, it'll unload the current page, load the new one and wait for data fetching even though we asked to do shallow routing.

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

¥When shallow routing is used with middleware it will not ensure the new page matches the current page like previously done without middleware. This is due to middleware being able to rewrite dynamically and can't be verified client-side without a data fetch which is skipped with shallow, so a shallow route change must always be treated as shallow.