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