Skip to main content

链接和导航

Next.js 有四种在路由之间导航的方法:

¥There are four ways to navigate between routes in Next.js:

本页将介绍如何使用每个选项,并深入探讨导航的工作原理。

¥This page will go through how to use each of these options, and dive deeper into how navigation works.

¥<Link> Component

<Link> 是一个内置组件,它扩展了 HTML <a> 标签以提供 prefetching 和客户端之间的路由导航。这是在 Next.js 中的路由之间导航的主要且推荐的方式。

¥<Link> is a built-in component that extends the HTML <a> tag to provide prefetching and client-side navigation between routes. It is the primary and recommended way to navigate between routes in Next.js.

你可以通过从 next/link 导入它并将 href 属性传递给组件来使用它:

¥You can use it by importing it from next/link, and passing a href prop to the component:

import Link from 'next/link'

export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
import Link from 'next/link'

export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}

还有其他可选属性可以传递给 <Link>。有关更多信息,请参阅 API 参考

¥There are other optional props you can pass to <Link>. See the API reference for more.

示例

¥Examples

链接到动态段

¥Linking to Dynamic Segments

当链接到 动态片段 时,可以使用 模板字面量和插值 生成链接列表。例如,要生成博客文章列表:

¥When linking to dynamic segments, you can use template literals and interpolation to generate a list of links. For example, to generate a list of blog posts:

import Link from 'next/link'

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

¥Checking Active Links

你可以使用 usePathname() 来确定链接是否处于活动状态。例如,要将一个类添加到活动链接,你可以检查当前的 pathname 是否与链接的 href 匹配:

¥You can use usePathname() to determine if a link is active. For example, to add a class to the active link, you can check if the current pathname matches the href of the link:

'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function Links() {
const pathname = usePathname()

return (
<nav>
<ul>
<li>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
</li>
<li>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</li>
</ul>
</nav>
)
}
'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function Links() {
const pathname = usePathname()

return (
<nav>
<ul>
<li>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
</li>
<li>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</li>
</ul>
</nav>
)
}

滚动到 id

¥Scrolling to an id

Next.js App Router 的默认行为是滚动到新路由的顶部或保持滚动位置以进行向后和向前导航。

¥The default behavior of the Next.js App Router is to scroll to the top of a new route or to maintain the scroll position for backwards and forwards navigation.

如果你想滚动到导航上的特定 id,你可以在你的 URL 附加 # 哈希链接,或者仅将哈希链接传递给 href 属性。这是可能的,因为 <Link> 渲染到 <a> 元素。

¥If you'd like to scroll to a specific id on navigation, you can append your URL with a # hash link or just pass a hash link to the href prop. This is possible since <Link> renders to an <a> element.

<Link href="/dashboard#settings">Settings</Link>

// Output
<a href="/dashboard#settings">Settings</a>

很高兴知道:

¥Good to know:

  • 如果导航时在视口中看不到 页面,Next.js 将滚动到 页面

    ¥Next.js will scroll to the Page if it is not visible in the viewport upon navigation.

禁用滚动恢复

¥Disabling scroll restoration

Next.js App Router 的默认行为是滚动到新路由的顶部或保持滚动位置以进行向后和向前导航。如果你想禁用此行为,可以将 scroll={false} 传递到 <Link> 组件,或者将 scroll: false 传递到 router.push()router.replace()

¥The default behavior of the Next.js App Router is to scroll to the top of a new route or to maintain the scroll position for backwards and forwards navigation. If you'd like to disable this behavior, you can pass scroll={false} to the <Link> component, or scroll: false to router.push() or router.replace().

// next/link
<Link href="/dashboard" scroll={false}>
Dashboard
</Link>
// useRouter
import { useRouter } from 'next/navigation'

const router = useRouter()

router.push('/dashboard', { scroll: false })

useRouter()

¥useRouter() hook

useRouter 钩子允许你以编程方式更改来自 客户端组件 的路由。

¥The useRouter hook allows you to programmatically change routes from Client Components.

'use client'

import { useRouter } from 'next/navigation'

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

return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}

有关 useRouter 方法的完整列表,请参阅 API 参考

¥For a full list of useRouter methods, see the API reference.

推荐:使用 <Link> 组件在路由之间导航,除非你有使用 useRouter 的特定要求。

¥Recommendation: Use the <Link> component to navigate between routes unless you have a specific requirement for using useRouter.

redirect 功能

¥redirect function

对于 服务器组件,请改用 redirect 函数。

¥For Server Components, use the redirect function instead.

import { redirect } from 'next/navigation'

async function fetchTeam(id: string) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}

export default async function Profile({ params }: { params: { id: string } }) {
const team = await fetchTeam(params.id)
if (!team) {
redirect('/login')
}

// ...
}
import { redirect } from 'next/navigation'

async function fetchTeam(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}

export default async function Profile({ params }) {
const team = await fetchTeam(params.id)
if (!team) {
redirect('/login')
}

// ...
}

很高兴知道:

¥Good to know:

  • redirect 默认返回 307(临时重定向)状态代码。当在服务器操作中使用时,它会返回 303(请参阅其他),通常用于由于 POST 请求而重定向到成功页面。

    ¥redirect returns a 307 (Temporary Redirect) status code by default. When used in a Server Action, it returns a 303 (See Other), which is commonly used for redirecting to a success page as a result of a POST request.

  • redirect 在内部抛出错误,因此应该在 try/catch 块之外调用它。

    ¥redirect internally throws an error so it should be called outside of try/catch blocks.

  • redirect 可以在渲染过程中在客户端组件中调用,但不能在事件处理程序中调用。你可以使用 useRouter 代替。

    ¥redirect can be called in Client Components during the rendering process but not in event handlers. You can use the useRouter hook instead.

  • redirect 还接受绝对 URL,可用于重定向到外部链接。

    ¥redirect also accepts absolute URLs and can be used to redirect to external links.

  • 如果你想在渲染过程之前重定向,请使用 next.config.js中间件

    ¥If you'd like to redirect before the render process, use next.config.js or Middleware.

请参阅 redirect API 参考 了解更多信息。

¥See the redirect API reference for more information.

使用原生历史 API

¥Using the native History API

Next.js 允许你使用原生 window.history.pushStatewindow.history.replaceState 方法来更新浏览器的历史堆栈,而无需重新加载页面。

¥Next.js allows you to use the native window.history.pushState and window.history.replaceState methods to update the browser's history stack without reloading the page.

pushStatereplaceState 调用集成到 Next.js 路由中,允许你与 usePathnameuseSearchParams 同步。

¥pushState and replaceState calls integrate into the Next.js Router, allowing you to sync with usePathname and useSearchParams.

window.history.pushState

使用它可以将新条目添加到浏览器的历史记录堆栈中。用户可以导航回之前的状态。例如,要对产品列表进行排序:

¥Use it to add a new entry to the browser's history stack. The user can navigate back to the previous state. For example, to sort a list of products:

'use client'

import { useSearchParams } from 'next/navigation'

export default function SortProducts() {
const searchParams = useSearchParams()

function updateSorting(sortOrder: string) {
const params = new URLSearchParams(searchParams.toString())
params.set('sort', sortOrder)
window.history.pushState(null, '', `?${params.toString()}`)
}

return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
)
}
'use client'

import { useSearchParams } from 'next/navigation'

export default function SortProducts() {
const searchParams = useSearchParams()

function updateSorting(sortOrder) {
const params = new URLSearchParams(searchParams.toString())
params.set('sort', sortOrder)
window.history.pushState(null, '', `?${params.toString()}`)
}

return (
<>
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
</>
)
}

window.history.replaceState

用它来替换浏览器历史记录堆栈上的当前条目。用户无法导航回之前的状态。例如,要切换应用的区域设置:

¥Use it to replace the current entry on the browser's history stack. The user is not able to navigate back to the previous state. For example, to switch the application's locale:

'use client'

import { usePathname } from 'next/navigation'

export function LocaleSwitcher() {
const pathname = usePathname()

function switchLocale(locale: string) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`
window.history.replaceState(null, '', newPath)
}

return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
)
}
'use client'

import { usePathname } from 'next/navigation'

export function LocaleSwitcher() {
const pathname = usePathname()

function switchLocale(locale) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`
window.history.replaceState(null, '', newPath)
}

return (
<>
<button onClick={() => switchLocale('en')}>English</button>
<button onClick={() => switchLocale('fr')}>French</button>
</>
)
}

路由和导航的工作原理

¥How Routing and Navigation Works

App Router 使用混合方法进行路由和导航。在服务器上,你的应用代码将自动按路由段 code-split。在客户端上,Next.js prefetchescaches 是路由段。这意味着,当用户导航到新路由时,浏览器不会重新加载页面,只有发生更改的路由段才会重新渲染 - 改善导航体验和性能。

¥The App Router uses a hybrid approach for routing and navigation. On the server, your application code is automatically code-split by route segments. And on the client, Next.js prefetches and caches the route segments. This means, when a user navigates to a new route, the browser doesn't reload the page, and only the route segments that change re-render - improving the navigation experience and performance.

1. 代码分割

¥ Code Splitting

代码分割允许你将应用代码分割成更小的包,以供浏览器下载和执行。这减少了每个请求的传输数据量和执行时间,从而提高了性能。

¥Code splitting allows you to split your application code into smaller bundles to be downloaded and executed by the browser. This reduces the amount of data transferred and execution time for each request, leading to improved performance.

服务器组件 允许你的应用代码自动按路由段进行代码分割。这意味着导航时仅加载当前路由所需的代码。

¥Server Components allow your application code to be automatically code-split by route segments. This means only the code needed for the current route is loaded on navigation.

2. 预请求

¥ Prefetching

预取是一种在用户访问路由之前在后台预加载路由的方法。

¥Prefetching is a way to preload a route in the background before the user visits it.

Next.js 中预取路由的方式有两种:

¥There are two ways routes are prefetched in Next.js:

  • <Link> 组件:当路由在用户视口中可见时,会自动预取路由。当页面首次加载或通过滚动进入视图时,会发生预取。

    ¥**<Link> component**: Routes are automatically prefetched as they become visible in the user's viewport. Prefetching happens when the page first loads or when it comes into view through scrolling.

  • router.prefetch()useRouter 钩子可用于以编程方式预取路由。

    ¥**router.prefetch()**: The useRouter hook can be used to prefetch routes programmatically.

<Link> 的默认预取行为(即,当 prefetch 属性未指定或设置为 null 时)根据你对 loading.js 的使用情况而有所不同。只有共享布局,从渲染的 "tree" 组件开始,直到第一个 loading.js 文件,才会为 30s 预取和缓存。这降低了获取整个动态路由的成本,这意味着你可以显示 即时加载状态,以便为用户提供更好的视觉反馈。

¥The <Link>'s default prefetching behavior (i.e. when the prefetch prop is left unspecified or set to null) is different depending on your usage of loading.js. Only the shared layout, down the rendered "tree" of components until the first loading.js file, is prefetched and cached for 30s. This reduces the cost of fetching an entire dynamic route, and it means you can show an instant loading state for better visual feedback to users.

你可以通过将 prefetch 属性设置为 false 来禁用预取。或者,你可以通过将 prefetch 属性设置为 true 来预取超出加载边界的整页数据。

¥You can disable prefetching by setting the prefetch prop to false. Alternatively, you can prefetch the full page data beyond the loading boundaries by setting the prefetch prop to true.

请参阅 <Link> API 参考 了解更多信息。

¥See the <Link> API reference for more information.

很高兴知道:

¥Good to know:

  • 预取在开发中不启用,仅在生产中启用。

    ¥Prefetching is not enabled in development, only in production.

3. 缓存

¥ Caching

Next.js 有一个名为 路由缓存 的内存客户端缓存。当用户在应用中导航时,prefetched 路由段和访问过的路由的 React Server 组件有效负载将存储在缓存中。

¥Next.js has an in-memory client-side cache called the Router Cache. As users navigate around the app, the React Server Component Payload of prefetched route segments and visited routes are stored in the cache.

这意味着在导航时,缓存会被尽可能地重用,而不是向服务器发出新的请求 - 通过减少请求和传输的数据数量来提高性能。

¥This means on navigation, the cache is reused as much as possible, instead of making a new request to the server - improving performance by reducing the number of requests and data transferred.

了解有关 路由缓存 工作原理以及配置方法的更多信息。

¥Learn more about how the Router Cache works and how to configure it.

4. 部分渲染

¥ Partial Rendering

部分渲染意味着仅在客户端上重新渲染导航时发生变化的路由段,并且保留所有共享段。

¥Partial rendering means only the route segments that change on navigation re-render on the client, and any shared segments are preserved.

例如,当在两条同级路由 /dashboard/settings/dashboard/analytics 之间导航时,将渲染 settingsanalytics 页面,并保留共享的 dashboard 布局。

¥For example, when navigating between two sibling routes, /dashboard/settings and /dashboard/analytics, the settings and analytics pages will be rendered, and the shared dashboard layout will be preserved.

如果没有部分渲染,每次导航都会导致整个页面在客户端上重新渲染。仅渲染发生变化的段可以减少传输的数据量和执行时间,从而提高性能。

¥Without partial rendering, each navigation would cause the full page to re-render on the client. Rendering only the segment that changes reduces the amount of data transferred and execution time, leading to improved performance.

5. 软导航

¥ Soft Navigation

浏览器在页面之间导航时执行 "硬导航"。Next.js App Router 在页面之间启用 "软导航",确保仅重新渲染已更改的路由段(部分渲染)。这使得客户端 React 状态能够在导航期间保留。

¥Browsers perform a "hard navigation" when navigating between pages. The Next.js App Router enables "soft navigation" between pages, ensuring only the route segments that have changed are re-rendered (partial rendering). This enables client React state to be preserved during navigation.

6. 后退和前进导航

¥ Back and Forward Navigation

默认情况下,Next.js 将保持向后和向前导航的滚动位置,并重用 路由缓存 中的路由段。

¥By default, Next.js will maintain the scroll position for backwards and forwards navigation, and re-use route segments in the Router Cache.

7. pages/app/ 之间的路由

¥ Routing between pages/ and app/

当从 pages/ 增量迁移到 app/ 时,Next.js 路由将自动处理两者之间的硬导航。为了检测从 pages/app/ 的转换,有一个客户端路由过滤器利用应用路由的概率检查,这有时会导致误报。默认情况下,这种情况发生的情况应该非常罕见,因为我们将误报可能性配置为 0.01%。可以通过 next.config.js 中的 experimental.clientRouterFilterAllowedRate 选项自定义此可能性。需要注意的是,降低误报率会增加客户端包中生成的过滤器的大小。

¥When incrementally migrating from pages/ to app/, the Next.js router will automatically handle hard navigation between the two. To detect transitions from pages/ to app/, there is a client router filter that leverages probabilistic checking of app routes, which can occasionally result in false positives. By default, such occurrences should be very rare, as we configure the false positive likelihood to be 0.01%. This likelihood can be customized via the experimental.clientRouterFilterAllowedRate option in next.config.js. It's important to note that lowering the false positive rate will increase the size of the generated filter in the client bundle.

或者,如果你希望完全禁用此处理并手动管理 pages/app/ 之间的路由,则可以在 next.config.js 中将 experimental.clientRouterFilter 设置为 false。禁用此功能后,默认情况下,页面中与应用路由重叠的任何动态路由将无法正确导航。

¥Alternatively, if you prefer to disable this handling completely and manage the routing between pages/ and app/ manually, you can set experimental.clientRouterFilter to false in next.config.js. When this feature is disabled, any dynamic routes in pages that overlap with app routes won't be navigated to properly by default.