Next.js导航与链接 #
一、Link组件 #
1.1 基本用法 #
tsx
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/">首页</Link>
<Link href="/about">关于</Link>
<Link href="/blog">博客</Link>
</nav>
)
}
1.2 Link属性 #
tsx
<Link
href="/dashboard"
className="nav-link"
prefetch={true}
replace={false}
scroll={true}
>
仪表盘
</Link>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| href | string/object | 必填 | 目标路径 |
| replace | boolean | false | 替换历史记录 |
| scroll | boolean | true | 导航时滚动到顶部 |
| prefetch | boolean/null | null | 预加载页面 |
| shallow | boolean | false | 浅层路由更新 |
1.3 动态链接 #
tsx
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>
)
}
1.4 对象形式路径 #
tsx
<Link
href={{
pathname: '/search',
query: { q: 'nextjs', category: 'tech' },
}}
>
搜索 Next.js
</Link>
1.5 带样式的Link #
tsx
import Link from 'next/link'
import { cn } from '@/lib/utils'
interface NavLinkProps {
href: string
children: React.ReactNode
active?: boolean
}
export function NavLink({ href, children, active }: NavLinkProps) {
return (
<Link
href={href}
className={cn(
'px-4 py-2 rounded-md transition-colors',
active
? 'bg-blue-500 text-white'
: 'text-gray-600 hover:bg-gray-100'
)}
>
{children}
</Link>
)
}
二、编程式导航 #
2.1 useRouter Hook #
tsx
'use client'
import { useRouter } from 'next/navigation'
export default function LoginForm() {
const router = useRouter()
const handleLogin = async () => {
const success = await login()
if (success) {
router.push('/dashboard')
}
}
return (
<button onClick={handleLogin}>
登录
</button>
)
}
2.2 useRouter方法 #
tsx
'use client'
import { useRouter } from 'next/navigation'
export default function NavigationButtons() {
const router = useRouter()
return (
<div className="flex gap-2">
<button onClick={() => router.push('/about')}>
前往关于页
</button>
<button onClick={() => router.replace('/login')}>
替换为登录页
</button>
<button onClick={() => router.back()}>
返回上一页
</button>
<button onClick={() => router.forward()}>
前进
</button>
<button onClick={() => router.refresh()}>
刷新当前路由
</button>
</div>
)
}
| 方法 | 说明 |
|---|---|
| push(href) | 导航到新页面 |
| replace(href) | 替换当前页面 |
| back() | 返回上一页 |
| forward() | 前进一页 |
| refresh() | 刷新当前路由 |
2.3 服务端重定向 #
tsx
import { redirect } from 'next/navigation'
export default async function Page() {
const session = await getSession()
if (!session) {
redirect('/login')
}
return <div>受保护的内容</div>
}
2.4 永久重定向 #
tsx
import { permanentRedirect } from 'next/navigation'
export default function OldPage() {
permanentRedirect('/new-page')
}
三、路由预加载 #
3.1 自动预加载 #
Link组件默认会预加载视口内的链接:
tsx
<Link href="/dashboard">
仪表盘
</Link>
3.2 禁用预加载 #
tsx
<Link href="/dashboard" prefetch={false}>
仪表盘
</Link>
3.3 手动预加载 #
tsx
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export default function usePrefetch(path: string) {
const router = useRouter()
useEffect(() => {
router.prefetch(path)
}, [router, path])
}
3.4 预加载策略 #
tsx
<Link href="/dashboard" prefetch={null}>
默认预加载(视口内)
</Link>
<Link href="/dashboard" prefetch={true}>
始终预加载
</Link>
<Link href="/dashboard" prefetch={false}>
禁用预加载
</Link>
四、滚动行为 #
4.1 默认滚动 #
导航时自动滚动到页面顶部:
tsx
<Link href="/about">
关于我们
</Link>
4.2 禁用滚动 #
保持当前滚动位置:
tsx
<Link href="/about" scroll={false}>
关于我们
</Link>
4.3 滚动到锚点 #
tsx
<Link href="/about#team">
团队介绍
</Link>
4.4 编程式滚动控制 #
tsx
'use client'
import { useRouter } from 'next/navigation'
export default function Navigation() {
const router = useRouter()
const navigateWithScroll = (path: string) => {
router.push(path)
setTimeout(() => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}, 100)
}
return (
<button onClick={() => navigateWithScroll('/about')}>
关于我们
</button>
)
}
五、获取路由信息 #
5.1 usePathname #
tsx
'use client'
import { usePathname } from 'next/navigation'
export default function CurrentPath() {
const pathname = usePathname()
return <p>当前路径: {pathname}</p>
}
5.2 useSearchParams #
tsx
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchPage() {
const searchParams = useSearchParams()
const query = searchParams.get('q')
const category = searchParams.get('category')
const page = searchParams.get('page') || '1'
const tags = searchParams.getAll('tag')
return (
<div>
<p>搜索: {query}</p>
<p>分类: {category}</p>
<p>页码: {page}</p>
<p>标签: {tags.join(', ')}</p>
</div>
)
}
5.3 useSelectedLayoutSegment #
tsx
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Sidebar() {
const segment = useSelectedLayoutSegment()
return (
<nav>
<a className={segment === 'dashboard' ? 'active' : ''}>
仪表盘
</a>
<a className={segment === 'settings' ? 'active' : ''}>
设置
</a>
</nav>
)
}
六、导航组件模式 #
6.1 导航栏组件 #
tsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
const navItems = [
{ href: '/', label: '首页' },
{ href: '/about', label: '关于' },
{ href: '/blog', label: '博客' },
{ href: '/contact', label: '联系' },
]
export default function Navigation() {
const pathname = usePathname()
return (
<nav className="bg-white border-b">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<Link href="/" className="text-xl font-bold">
Logo
</Link>
<div className="flex gap-6">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={`
px-3 py-2 rounded-md text-sm font-medium
${pathname === item.href
? 'bg-gray-100 text-gray-900'
: 'text-gray-600 hover:text-gray-900'
}
`}
>
{item.label}
</Link>
))}
</div>
</div>
</div>
</nav>
)
}
6.2 面包屑导航 #
tsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
const routeNames: Record<string, string> = {
'': '首页',
'about': '关于',
'blog': '博客',
'contact': '联系',
'products': '产品',
}
export default function Breadcrumb() {
const pathname = usePathname()
const segments = pathname.split('/').filter(Boolean)
return (
<nav className="flex items-center gap-2 text-sm">
<Link href="/" className="text-blue-600 hover:underline">
首页
</Link>
{segments.map((segment, index) => {
const href = `/${segments.slice(0, index + 1).join('/')}`
const isLast = index === segments.length - 1
const name = routeNames[segment] || segment
return (
<span key={href} className="flex items-center gap-2">
<span className="text-gray-400">/</span>
{isLast ? (
<span className="text-gray-500">{name}</span>
) : (
<Link href={href} className="text-blue-600 hover:underline">
{name}
</Link>
)}
</span>
)
})}
</nav>
)
}
6.3 分页组件 #
tsx
'use client'
import Link from 'next/link'
import { useSearchParams } from 'next/navigation'
interface PaginationProps {
totalPages: number
currentPage: number
}
export default function Pagination({ totalPages, currentPage }: PaginationProps) {
const searchParams = useSearchParams()
const createPageUrl = (page: number) => {
const params = new URLSearchParams(searchParams)
params.set('page', page.toString())
return `?${params.toString()}`
}
return (
<div className="flex items-center gap-2">
{currentPage > 1 && (
<Link href={createPageUrl(currentPage - 1)} className="px-3 py-1 border rounded">
上一页
</Link>
)}
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Link
key={page}
href={createPageUrl(page)}
className={`
px-3 py-1 border rounded
${page === currentPage
? 'bg-blue-500 text-white'
: 'hover:bg-gray-100'
}
`}
>
{page}
</Link>
))}
{currentPage < totalPages && (
<Link href={createPageUrl(currentPage + 1)} className="px-3 py-1 border rounded">
下一页
</Link>
)}
</div>
)
}
七、高级导航模式 #
7.1 导航守卫 #
tsx
'use client'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
export function useAuthGuard(redirectTo: string = '/login') {
const router = useRouter()
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null)
useEffect(() => {
const checkAuth = async () => {
const auth = await verifyAuth()
setIsAuthenticated(auth)
if (!auth) {
router.push(redirectTo)
}
}
checkAuth()
}, [router, redirectTo])
return isAuthenticated
}
7.2 导航事件 #
tsx
'use client'
import { usePathname, useSearchParams } from 'next/navigation'
import { useEffect } from 'react'
export function useNavigationEvents(onNavigate: (path: string) => void) {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = pathname + (searchParams.toString() ? `?${searchParams}` : '')
onNavigate(url)
}, [pathname, searchParams, onNavigate])
}
7.3 导航确认 #
tsx
'use client'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
export function useConfirmNavigation(message: string) {
const router = useRouter()
const [pendingUrl, setPendingUrl] = useState<string | null>(null)
const navigate = (url: string) => {
if (confirm(message)) {
router.push(url)
}
}
return { navigate }
}
八、最佳实践 #
8.1 导航性能 #
tsx
<Link href="/dashboard" prefetch={true}>
仪表盘
</Link>
8.2 可访问性 #
tsx
<Link
href="/about"
aria-label="了解更多关于我们的信息"
aria-current={isActive ? 'page' : undefined}
>
关于我们
</Link>
8.3 键盘导航 #
tsx
<Link
href="/about"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.currentTarget.click()
}
}}
>
关于我们
</Link>
九、总结 #
导航与链接要点:
| 要点 | 说明 |
|---|---|
| Link组件 | 声明式导航 |
| useRouter | 编程式导航 |
| 预加载 | prefetch属性 |
| 滚动行为 | scroll属性 |
| 路由信息 | usePathname/useSearchParams |
| 导航组件 | 封装可复用组件 |
下一步,让我们学习错误处理!
最后更新:2026-03-28