next/router 与 next/navigation 对比
问题概述
在 Next.js 开发中,开发者经常发现项目既存在 next/router
又存在 next/navigation
两个包,它们都提供了相似的 useRouter()
钩子,但返回的对象结构和行为却存在显著差异。为什么 Next.js 要在两个不同的包中提供看似相同的路由功能?这实际上是 Next.js 路由架构迭代的直接体现,两种路由系统对应不同的项目结构,混淆使用将导致运行时错误。
bash
# 典型的错误信息 (App Router中使用next/router时)
Error: NextRouter was not mounted.
https://nextjs.org/docs/messages/next-router-not-mounted
核心区别解析
1. 设计目的与适用场景
next/router
:- 专为 Pages Router 设计(传统
pages
目录结构) - 用于基础路由需求,如页面导航和参数获取
- 与类组件/函数组件兼容
jsx// pages 目录结构中使用 import { useRouter } from 'next/router'; function PageComponent() { const router = useRouter(); const { id } = router.query; // 获取URL参数 return <div>Page ID: {id}</div>; }
- 专为 Pages Router 设计(传统
next/navigation
:- 专为 App Router 设计(
app
目录结构,Next.js 13+) - 支持 服务端组件 和 客户端组件
- 提供更轻量级API,支持服务器端操作(如
redirect
,notFound
)
jsx// app 目录结构中使用 import { useRouter } from 'next/navigation'; export default function Component() { const router = useRouter(); return ( <button onClick={() => router.push('/dashboard')}> 跳转到控制台 </button> ); }
- 专为 App Router 设计(
2. API 差异关键点
特性 | next/router (Pages) | next/navigation (App) |
---|---|---|
路由方法 | push() , replace() 完整 | push() , replace() 精简 |
参数获取 | router.query 对象 | useSearchParams() 钩子 |
路由事件 | 支持 routeChangeStart 等 | 不提供事件监听 API |
服务端能力 | 仅客户端使用 | 支持服务器端操作(重定向等) |
静态生成兼容性 | 完全支持 | 为 App Router 优化设计 |
3. 混用导致的常见错误
当在 App Router 项目中错误导入 next/router
:
jsx
// ❌ 错误示例 (App Router 中)
import { useRouter } from 'next/router'; // 错误来源
export default function AppComponent() {
const router = useRouter(); // 报错:NextRouter未挂载
// ...
}
关键警告
从 Next.js 13 起,在 App Router 结构中 next/router
已被弃用。继续使用将导致 NextRouter was not mounted
运行时错误。
迁移与最佳实践
从 Pages Router 迁移到 App Router
替换导入路径:
diff- import { useRouter } from 'next/router'; + import { useRouter } from 'next/navigation';
API 调整:
- 路由参数改用
useSearchParams()
获取
jsx// 在 App Router 中获取URL参数 import { useSearchParams } from 'next/navigation'; export default function Page() { const searchParams = useSearchParams(); const id = searchParams.get('id'); }
- 路由参数改用
服务端操作使用独立函数:
jsx// 服务端组件中直接使用重定向 import { redirect } from 'next/navigation'; export default async function Page() { const data = await fetchData(); if (!data) redirect('/login'); // 服务端重定向 }
项目策略建议
- 新项目:直接使用 App Router +
next/navigation
- 旧项目:
- 继续使用 Pages Router +
next/router
(仍被支持) - 逐步迁移到 App Router,替换路由相关代码
- 继续使用 Pages Router +
技术底层差异
架构层面 | Pages Router | App Router |
---|---|---|
路由处理 | 客户端主导 | 服务端/客户端混合渲染 |
打包大小 | 包含完整路由历史功能 | 更精简易拆包 |
数据获取 | getServerSideProps 等 | 直接在组件内 async/await |
布局系统 | 自定义 _app.js 实现 | 原生支持布局嵌套 |
总结决策指南
目录结构决定路由包:
pages/
目录 →next/router
app/
目录 →next/navigation
Next 13+ 新项目:优先选择 App Router + next/navigation 组合
- 支持 React 最新特性
- 更适合服务端组件架构
- 获得框架长期支持
维护旧项目:坚持使用 Pages Router 无需立即迁移,除非需要:
- 服务端组件功能
- 更优的布局系统
- 混合渲染能力升级
最后需要强调的是,两者并非功能增强关系,而是架构适配方案。选择取决于项目使用的路由模式,而非功能强弱。随着 Next.js 的发展,App Router 和 next/navigation
已成为官方推荐的未来方向。