Skip to content

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>;
    }
  • 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>
      );
    }

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

  1. 替换导入路径

    diff
    - import { useRouter } from 'next/router';
    + import { useRouter } from 'next/navigation';
  2. API 调整

    • 路由参数改用 useSearchParams() 获取
    jsx
    // 在 App Router 中获取URL参数
    import { useSearchParams } from 'next/navigation';
    
    export default function Page() {
      const searchParams = useSearchParams();
      const id = searchParams.get('id');
    }
  3. 服务端操作使用独立函数

    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 RouterApp Router
路由处理客户端主导服务端/客户端混合渲染
打包大小包含完整路由历史功能更精简易拆包
数据获取getServerSideProps直接在组件内 async/await
布局系统自定义 _app.js 实现原生支持布局嵌套

总结决策指南

  1. 目录结构决定路由包

    • pages/ 目录 → next/router
    • app/ 目录 → next/navigation
  2. Next 13+ 新项目:优先选择 App Router + next/navigation 组合

    • 支持 React 最新特性
    • 更适合服务端组件架构
    • 获得框架长期支持
  3. 维护旧项目:坚持使用 Pages Router 无需立即迁移,除非需要:

    • 服务端组件功能
    • 更优的布局系统
    • 混合渲染能力升级

最后需要强调的是,两者并非功能增强关系,而是架构适配方案。选择取决于项目使用的路由模式,而非功能强弱。随着 Next.js 的发展,App Router 和 next/navigation 已成为官方推荐的未来方向。