Skip to main content

链接

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

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

基本用法:

¥Basic usage:

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

参考

¥Reference

可以将以下 props 传递给 <Link> 组件:

¥The following props can be passed to the <Link> component:

属性示例类型必需的
hrefhref="/dashboard"字符串或对象是的
replacereplace={false}布尔值*
scrollscroll={false}布尔值*
prefetchprefetch={false}布尔值或空值*

很高兴知道:<a> 标签属性(例如 classNametarget="_blank")可以作为 props 添加到 <Link> 中,并将传递到底层 <a> 元素。

¥Good to know: <a> tag attributes such as className or target="_blank" can be added to <Link> as props and will be passed to the underlying <a> element.

href(必需的)

¥href (required)

要导航到的路径或 URL。

¥The path or URL to navigate to.

import Link from 'next/link'

// Navigate to /about?name=test
export default function Page() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
About
</Link>
)
}
import Link from 'next/link'

// Navigate to /about?name=test
export default function Page() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
About
</Link>
)
}

replace

默认为 false。当 true 时,next/link 会替换当前的历史状态,而不是在 浏览器的历史 堆栈中添加新的 URL。

¥Defaults to false. When true, next/link will replace the current history state instead of adding a new URL into the browser's history stack.

import Link from 'next/link'

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

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

scroll

默认为 true。Next.js 中 <Link> 的默认滚动行为是保持滚动位置,类似于浏览器处理前后导航的方式。当你导航到新的 页面 时,只要页面在视口中可见,滚动位置就会保持不变。但是,如果页面在视口中不可见,Next.js 将滚动到第一个页面元素的顶部。

¥Defaults to true. The default scrolling behavior of <Link> in Next.js is to maintain scroll position, similar to how browsers handle back and forwards navigation. When you navigate to a new Page, scroll position will stay the same as long as the Page is visible in the viewport. However, if the Page is not visible in the viewport, Next.js will scroll to the top of the first Page element.

scroll = {false} 时,Next.js 将不会尝试滚动到第一个 Page 元素。

¥When scroll = {false}, Next.js will not attempt to scroll to the first Page element.

很高兴知道:Next.js 在管理滚动行为之前检查 scroll: false。如果启用了滚动,它会识别导航的相关 DOM 节点并检查每个顶层元素。所有不可滚动的元素和没有渲染 HTML 的元素都将被绕过,这包括粘性或固定定位的元素,以及不可见的元素(例如使用 getBoundingClientRect 计算的元素)。Next.js 然后继续遍历同级元素,直到它识别出在视口中可见的可滚动元素。

¥Good to know: Next.js checks if scroll: false before managing scroll behavior. If scrolling is enabled, it identifies the relevant DOM node for navigation and inspects each top-level element. All non-scrollable elements and those without rendered HTML are bypassed, this includes sticky or fixed positioned elements, and non-visible elements such as those calculated with getBoundingClientRect. Next.js then continues through siblings until it identifies a scrollable element that is visible in the viewport.

import Link from 'next/link'

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

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

prefetch

<Link /> 组件进入用户的视口(最初或通过滚动)时,会发生预取。Next.js 在后台预取并加载链接的路由(用 href 表示)及其数据,以提高客户端导航的性能。如果用户将鼠标悬停在 <Link /> 上时预取的数据已过期,Next.js 将尝试再次预取它。预取仅在生产中启用。

¥Prefetching happens when a <Link /> component enters the user's viewport (initially or through scroll). Next.js prefetches and loads the linked route (denoted by the href) and its data in the background to improve the performance of client-side navigations. If the prefetched data has expired by the time the user hovers over a <Link />, Next.js will attempt to prefetch it again. Prefetching is only enabled in production.

可以将以下值传递给 prefetch prop:

¥The following values can be passed to the prefetch prop:

  • null(默认):预取行为取决于路由是静态还是动态。对于静态路由,将预取完整路由(包括其所有数据)。对于动态路由,将预取到具有 loading.js 边界的最近路段的部分路由。

    ¥**null (default)**: Prefetch behavior depends on whether the route is static or dynamic. For static routes, the full route will be prefetched (including all its data). For dynamic routes, the partial route down to the nearest segment with a loading.js boundary will be prefetched.

  • true:将为静态和动态路由预取完整路由。

    ¥true: The full route will be prefetched for both static and dynamic routes.

  • false:进入视口和悬停时都不会发生预取。

    ¥false: Prefetching will never happen both on entering the viewport and on hover.

import Link from 'next/link'

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

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

示例

¥Examples

以下示例演示了如何在不同场景中使用 <Link> 组件。

¥The following examples demonstrate how to use the <Link> component in different scenarios.

链接到动态段

¥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'

interface Post {
id: number
title: string
slug: string
}

export default function PostList({ posts }: { posts: Post[] }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
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>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>

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

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

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

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

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

滚动到 id

¥Scrolling to an id

如果你想滚动到导航上的特定 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.

链接到动态路由段

¥Linking to dynamic route segments

对于 动态路由段,使用模板文字来创建链接的路径会很方便。

¥For dynamic route segments, it can be handy to use template literals to create the link's path.

例如,你可以生成指向动态路由 app/blog/[slug]/page.js 的链接列表:

¥For example, you can generate a list of links to the dynamic route app/blog/[slug]/page.js:

import Link from 'next/link'

export default function Page({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
import Link from 'next/link'

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

如果子组件是封装 <a> 标签的自定义组件

¥If the child is a custom component that wraps an <a> tag

如果 Link 的子组件是封装 <a> 标签的自定义组件,则必须将 passHref 添加到 Link。如果你使用像 styled-components 这样的库,这是必要的。如果没有这个,<a> 标签将不会有 href 属性,这会损害你网站的可访问性并可能影响 SEO。如果你使用 ESLint,则有一个内置规则 next/link-passhref 可确保正确使用 passHref

¥If the child of Link is a custom component that wraps an <a> tag, you must add passHref to Link. This is necessary if you’re using libraries like styled-components. Without this, the <a> tag will not have the href attribute, which hurts your site's accessibility and might affect SEO. If you're using ESLint, there is a built-in rule next/link-passhref to ensure correct usage of passHref.

import Link from 'next/link'
import styled from 'styled-components'

// This creates a custom component that wraps an <a> tag
const RedLink = styled.a`
color: red;
`

function NavLink({ href, name }) {
return (
<Link href={href} passHref legacyBehavior>
<RedLink>{name}</RedLink>
</Link>
)
}

export default NavLink
import Link from 'next/link'
import styled from 'styled-components'

// This creates a custom component that wraps an <a> tag
const RedLink = styled.a`
color: red;
`

function NavLink({ href, name }) {
return (
<Link href={href} passHref legacyBehavior>
<RedLink>{name}</RedLink>
</Link>
)
}

export default NavLink
  • 如果你使用 emotion 的 JSX pragma 功能 (@jsx jsx),则即使直接使用 <a> 标记,也必须使用 passHref

    ¥If you’re using emotion’s JSX pragma feature (@jsx jsx), you must use passHref even if you use an <a> tag directly.

  • 组件应支持 onClick 属性以正确触发导航。

    ¥The component should support onClick property to trigger navigation correctly.

嵌套功能组件

¥Nesting a functional component

如果 Link 的子组件是功能组件,除了使用 passHreflegacyBehavior 之外,还必须将该组件封装在 React.forwardRef 中:

¥If the child of Link is a functional component, in addition to using passHref and legacyBehavior, you must wrap the component in React.forwardRef:

import Link from 'next/link'
import React from 'react'

// Define the props type for MyButton
interface MyButtonProps {
onClick?: React.MouseEventHandler<HTMLAnchorElement>
href?: string
}

// Use React.ForwardRefRenderFunction to properly type the forwarded ref
const MyButton: React.ForwardRefRenderFunction<
HTMLAnchorElement,
MyButtonProps
> = ({ onClick, href }, ref) => {
return (
<a href={href} onClick={onClick} ref={ref}>
Click Me
</a>
)
}

// Use React.forwardRef to wrap the component
const ForwardedMyButton = React.forwardRef(MyButton)

export default function Page() {
return (
<Link href="/about" passHref legacyBehavior>
<ForwardedMyButton />
</Link>
)
}
import Link from 'next/link'
import React from 'react'

// `onClick`, `href`, and `ref` need to be passed to the DOM element
// for proper handling
const MyButton = React.forwardRef(({ onClick, href }, ref) => {
return (
<a href={href} onClick={onClick} ref={ref}>
Click Me
</a>
)
})

// Add a display name for the component (useful for debugging)
MyButton.displayName = 'MyButton'

export default function Page() {
return (
<Link href="/about" passHref legacyBehavior>
<MyButton />
</Link>
)
}

替换 URL 而不是推送

¥Replace the URL instead of push

Link 组件的默认行为是将 push 新 URL 放入 history 堆栈中。你可以使用 replace 属性来防止添加新条目,如下例所示:

¥The default behavior of the Link component is to push a new URL into the history stack. You can use the replace prop to prevent adding a new entry, as in the following example:

import Link from 'next/link'

export default function Page() {
return (
<Link href="/about" replace>
About us
</Link>
)
}
import Link from 'next/link'

export default function Page() {
return (
<Link href="/about" replace>
About us
</Link>
)
}

禁用滚动到页面顶部

¥Disable scrolling to the top of the page

Next.js 中 <Link> 的默认滚动行为是保持滚动位置,类似于浏览器处理前后导航的方式。当你导航到新的 页面 时,只要页面在视口中可见,滚动位置就会保持不变。

¥The default scrolling behavior of <Link> in Next.js is to maintain scroll position, similar to how browsers handle back and forwards navigation. When you navigate to a new Page, scroll position will stay the same as long as the Page is visible in the viewport.

但是,如果页面在视口中不可见,Next.js 将滚动到第一个页面元素的顶部。如果你想禁用此行为,可以将 scroll={false} 传递到 <Link> 组件,或者将 scroll: false 传递到 router.push()router.replace()

¥However, if the Page is not visible in the viewport, Next.js will scroll to the top of the first Page element. 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().

import Link from 'next/link'

export default function Page() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}
import Link from 'next/link'

export default function Page() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}

使用 router.push()router.replace()

¥Using router.push() or router.replace():

// useRouter
import { useRouter } from 'next/navigation'

const router = useRouter()

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

¥Prefetching links in Middleware

通常将 中间件 用于身份验证或涉及将用户重写到不同页面的其他目的。为了让 <Link /> 组件通过中间件重写正确地预取链接,你需要告诉 Next.js 要显示的 URL 和要预取的 URL。这是为了避免不必要的中间件获取来了解预取的正确路径。

¥It's common to use Middleware for authentication or other purposes that involve rewriting the user to a different page. In order for the <Link /> component to properly prefetch links with rewrites via Middleware, you need to tell Next.js both the URL to display and the URL to prefetch. This is required to avoid un-necessary fetches to middleware to know the correct route to prefetch.

例如,如果你想要提供具有经过身份验证和访问者视图的 /dashboard 路由,则可以在中间件中添加以下内容以将用户重定向到正确的页面:

¥For example, if you want to serve a /dashboard route that has authenticated and visitor views, you can add the following in your Middleware to redirect the user to the correct page:

import { NextResponse } from 'next/server'

export function middleware(request: Request) {
const nextUrl = request.nextUrl
if (nextUrl.pathname === '/dashboard') {
if (request.cookies.authToken) {
return NextResponse.rewrite(new URL('/auth/dashboard', request.url))
} else {
return NextResponse.rewrite(new URL('/public/dashboard', request.url))
}
}
}
import { NextResponse } from 'next/server'

export function middleware(request) {
const nextUrl = request.nextUrl
if (nextUrl.pathname === '/dashboard') {
if (request.cookies.authToken) {
return NextResponse.rewrite(new URL('/auth/dashboard', request.url))
} else {
return NextResponse.rewrite(new URL('/public/dashboard', request.url))
}
}
}

在这种情况下,你需要在 <Link /> 组件中使用以下代码:

¥In this case, you would want to use the following code in your <Link /> component:

'use client'

import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // Your auth hook

export default function Page() {
const isAuthed = useIsAuthed()
const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
return (
<Link as="/dashboard" href={path}>
Dashboard
</Link>
)
}
'use client'

import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // Your auth hook

export default function Page() {
const isAuthed = useIsAuthed()
const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
return (
<Link as="/dashboard" href={path}>
Dashboard
</Link>
)
}

版本历史记录

¥Version history

版本变化
v13.0.0不再需要子 <a> 标签。提供 codemod 来自动更新你的代码库。
v10.0.0指向动态路由的 href 属性会自动解析,不再需要 as 属性。
v8.0.0改进了预取性能。
v1.0.0next/link 推出。