Next.js 16 入门教程
链接与导航
本教程共 42 篇 · 第 5 篇 · 更新于 2026-07-30 · 约 8 分钟阅读
Next.jsNext.js 16 入门教程导航LinkuseRouter路由
5. 链接与导航
本节目标:学会在 Next.js 页面间跳转的所有方式,理解预取、客户端转换和流式渲染如何让导航飞快。
导航的四个概念
Next.js 的导航优化建立在四个机制上:
- 服务端渲染:页面在服务器生成 HTML
- 预取:链接进入视口时提前加载
- 流式渲染:页面分段发送,不必等全部完成
- 客户端转换:跳转时只更新变化的部分,不刷新整个页面
理解这四个概念,才能用好导航 API。
Link 组件
<Link> 是 Next.js 导航的主要方式,替代原生 <a> 标签:
import Link from 'next/link'
export default function Nav() {
return (
<nav>
<Link href="/">首页</Link>
<Link href="/blog">博客</Link>
<Link href="/about">关于</Link>
</nav>
)
}
跳动态路由
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
带查询参数
<Link href={{
pathname: '/products',
query: { category: 'shoes', sort: 'price' },
}}>
鞋子
</Link>
会生成 /products?category=shoes&sort=price。
常用 Props
| Prop | 默认值 | 说明 |
|---|---|---|
href | 必填 | 跳转路径 |
replace | false | 替换当前历史记录,不新增 |
scroll | true | 是否滚动到页面顶部 |
prefetch | null | 是否预取(true/false/null) |
// 不滚动到顶部
<Link href="/dashboard" scroll={false}>仪表盘</Link>
// 替换历史记录(用户无法后退)
<Link href="/success" replace>提交成功</Link>
预取行为
prefetch 控制预取策略:
null(默认):静态路由全量预取,动态路由只预取到最近的loading.tsxtrue:始终全量预取false:不预取
// 禁用预取(适合大量链接的列表)
<Link href="/blog" prefetch={false}>博客</Link>
Note预取只在生产环境生效。开发模式下不会预取。
useRouter Hook
需要编程式导航时,用 useRouter:
'use client'
import { useRouter } from 'next/navigation'
export function Button() {
const router = useRouter()
return (
<button onClick={() => router.push('/dashboard')}>
跳转
</button>
)
}
常用方法
// 前进到新页面(新增历史记录)
router.push('/blog')
// 替换当前页面(不新增历史记录)
router.replace('/blog')
// 后退
router.back()
// 刷新当前页面(重新获取数据)
router.refresh()
带选项跳转
router.push('/dashboard', { scroll: false })
usePathname Hook
获取当前路径,常用于高亮当前导航项:
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export function NavLinks() {
const pathname = usePathname()
return (
<nav>
<Link
href="/"
className={pathname === '/' ? 'active' : ''}
>
首页
</Link>
<Link
href="/about"
className={pathname === '/about' ? 'active' : ''}
>
关于
</Link>
</nav>
)
}
useSearchParams Hook
读取 URL 查询参数:
'use client'
import { useSearchParams } from 'next/navigation'
export function SortButtons() {
const searchParams = useSearchParams()
function sort(order: string) {
const params = new URLSearchParams(searchParams.toString())
params.set('sort', order)
window.history.pushState(null, '', `?${params.toString()}`)
}
return (
<>
<button onClick={() => sort('asc')}>价格升序</button>
<button onClick={() => sort('desc')}>价格降序</button>
</>
)
}
原生 History API
Next.js 支持直接操作浏览器历史栈:
'use client'
// 新增历史记录
window.history.pushState(null, '', '?sort=asc')
// 替换当前记录
window.history.replaceState(null, '', '/en/about')
这些操作会同步更新 Next.js 路由器的状态。
导航优化
动态路由加 loading.tsx
没有 loading.tsx 的动态路由,导航时会白屏等待。加上它,用户立刻看到骨架屏:
// app/blog/[slug]/loading.tsx
export default function Loading() {
return <div className="animate-pulse">加载中...</div>
}
慢网络下的反馈
网络慢时,预取可能来不及完成。用 useLinkStatus 显示即时反馈:
'use client'
import { useLinkStatus } from 'next/link'
export function LoadingIndicator() {
const { pending } = useLinkStatus()
return (
<span className={pending ? 'visible' : 'invisible'}>
加载中...
</span>
)
}
禁用预取的场景
以下情况考虑禁用预取:
- 无限滚动列表(链接太多)
- 权限页面(用户可能没权限访问)
- 导出/下载链接
<Link href="/heavy-page" prefetch={false}>重页面</Link>
跨根布局导航
如果应用有多个根布局(通过路由组实现),跨根布局导航会触发完整页面加载,而不是客户端转换。
这是因为不同根布局的 <html> 结构可能不同,无法平滑过渡。
小结
这一章我们学了导航的完整方案:
<Link>组件:声明式导航,自动预取useRouter:编程式导航,支持 push/replace/back/refreshusePathname:获取当前路径,高亮导航项useSearchParams:读取查询参数loading.tsx:让动态路由导航即时响应
下一章,我们来学习 Next.js 最核心的概念——Server Components。