Skip to content

Next.js 提供了两种主要的路由系统:传统的 Pages Router 和较新的 App Router。它们在文件结构、数据获取方式、布局管理、渲染策略等方面有显著区别。App Router 是 Next.js 团队推荐的未来方向,它基于 React Server Components 构建,带来了许多现代化特性。以下是它们的主要区别:

1. 路由和文件结构:

  • Pages Router:

    • 基于 pages 目录。
    • 文件系统即路由:pages/about.js 对应 /about 路由。
    • 动态路由:pages/posts/[id].js 对应 /posts/123
    • API 路由:pages/api/hello.js 对应 /api/hello
    • 特殊文件:_app.js (全局App组件,用于布局和状态), _document.js (自定义HTML结构), _error.js (自定义错误页面)。
  • App Router:

    • 基于 app 目录。
    • 约定式路由,使用特定文件名来定义UI的不同部分:
      • page.js: 定义路由的 UI,是路由段可公开访问的部分。
      • layout.js: 定义共享 UI,包裹子路由或 page.js布局会保留状态,在导航时不会重新渲染。
      • template.js: 类似于 layout.js,但在导航时会重新挂载,状态不会保留。
      • loading.js: 定义加载 UI,使用 React Suspense 自动包裹 page.js 和其子组件。
      • error.js: 定义错误 UI,使用 React Error Boundary 自动包裹 page.js 和其子组件,用于捕获运行时错误并提供恢复机制。
      • global-error.js: 捕获根 layout.js 中的错误。
      • route.js: 定义 API 路由 (替代 pages/api)。
      • default.js: 定义并行路由(Parallel Routes)的默认回退 UI。
    • 文件夹即路由段 (Segments)app/dashboard/settings/page.js 对应 /dashboard/settings
    • 动态路由段:app/posts/[id]/page.js
    • 路由组 (Route Groups)(marketing)/about/page.js (marketing) 不会出现在 URL 中,用于组织路由或创建不同的根布局。
    • 并行路由 (Parallel Routes)拦截路由 (Intercepting Routes) 等高级路由模式。

2. 组件模型:

  • Pages Router:

    • 默认所有组件都是客户端组件 (Client Components)
    • 服务器端逻辑通过 getServerSidePropsgetStaticProps 执行,其结果作为 props 传递给页面组件。
  • App Router:

    • 引入了 React Server Components (RSC)
    • app 目录下的组件默认是服务器组件 (Server Components)
    • 服务器组件可以直接在组件内部使用 async/await 进行数据获取,代码只在服务器端运行,不会发送到客户端。
    • 需要交互性或浏览器 API 的组件,需要使用 "use client"; 指令标记为客户端组件 (Client Components)
    • 服务器组件可以将数据直接传递给客户端组件。

3. 数据获取:

  • Pages Router:

    • getStaticProps (SSG): 构建时获取数据。
    • getServerSideProps (SSR): 请求时获取数据。
    • getStaticPaths: 与 getStaticProps 结合用于动态路由的 SSG。
    • 客户端数据获取:在 useEffect 中使用 fetch 或 SWR/React Query 等库。
  • App Router:

    • 服务器组件中直接 async/await:
      javascript
      // app/posts/[id]/page.js
      async function getPost(id) {
        const res = await fetch(`https://.../posts/${id}`);
        return res.json();
      }
      
      export default async function PostPage({ params }) {
        const post = await getPost(params.id);
        return <div>{post.title}</div>;
      }
    • fetch API 被扩展,自动处理缓存和重新验证 (revalidation)。
    • 不再需要 getServerSidePropsgetStaticProps。数据获取逻辑更贴近组件本身。
    • 客户端组件中数据获取方式与 Pages Router 类似 (useEffect, SWR, React Query)。
    • Route Handlers (route.js) 用于创建 API 端点,替代 pages/api

4. 布局:

  • Pages Router:

    • 通过 _app.js 实现全局布局。
    • 嵌套布局通常需要手动在每个页面组件中包裹。
    • 页面导航时,整个页面会重新渲染,布局状态可能丢失(除非使用 persisted 布局模式,但较复杂)。
  • App Router:

    • 内置支持嵌套布局持久化布局
    • layout.js 文件定义的 UI 会在导航时保持状态,只渲染改变的部分。
    • 可以为不同的路由段创建独立的布局。
    • template.js 提供了在导航时重新渲染布局的选项。

5. 加载状态和流式传输:

  • Pages Router:

    • 加载状态通常需要手动管理 (例如,用 useState 控制加载指示器)。
    • 不支持开箱即用的流式 HTML 渲染。
  • App Router:

    • 通过 loading.js 文件,可以为路由段自动创建基于 React Suspense 的加载 UI。
    • 支持服务器端流式传输 (Streaming) HTML。这意味着用户可以更快地看到页面的部分内容,而无需等待整个页面加载完成,提升了感知性能。

6. 错误处理:

  • Pages Router:

    • _error.js 用于处理 404 和 500 等错误。
    • 组件内部错误需要使用 React Error Boundaries 手动处理。
  • App Router:

    • error.js 文件用于为特定路由段创建错误 UI。它会自动捕获该段及其子组件的错误,并允许尝试恢复。
    • global-error.js 用于处理根布局的错误。
    • 基于 React Error Boundaries,提供了更细粒度的错误控制。

7. SEO:

  • Pages Router:

    • 使用 next/head 组件在页面级别设置 <head> 标签(如 title, meta)。
  • App Router:

    • 提供了新的 metadata API。
    • 可以在 layout.jspage.js 中导出静态 metadata 对象,或动态生成元数据的 generateMetadata 函数。
    • 支持动态图片生成 (generateImageMetadata) 和 sitemap.xml, robots.txt 等。

8. 缓存和重新验证:

  • Pages Router:

    • 主要通过 getStaticPropsrevalidate 选项实现 ISR (Incremental Static Regeneration)。
    • getServerSideProps 页面默认不缓存,除非配置 CDN。
  • App Router:

    • fetch API 进行了扩展,支持更细粒度的缓存控制(cache 选项:force-cache, no-store 等)和按需/基于时间的重新验证 (next: { revalidate: number }tags)。
    • Route Segment Config: 可以在 page.jslayout.js 中导出配置,如 export const revalidate = 60;
    • 提供了更灵活和强大的缓存策略。

核心区别总结:

特性Pages RouterApp Router
目录pagesapp
组件默认客户端组件服务器组件
数据获取getServerSideProps, getStaticProps服务器组件内 async/await, 扩展的 fetch
布局_app.js,手动嵌套layout.js (持久化), template.js (重渲染), 自动嵌套
加载 UI手动管理loading.js (基于 Suspense)
错误处理_error.js, 手动 Error Boundarieserror.js, global-error.js (自动 Error Boundaries)
流式传输不支持支持 (Streaming HTML)
API 路由pages/apiroute.js (Route Handlers)
SEO Metanext/headmetadata 对象或 generateMetadata 函数
缓存ISR (revalidate in GSP)扩展 fetch, Route Segment Config, 按需/标签重新验证
现代化特性较少直接利用 React 新特性深度集成 React Server Components, Suspense, Transitions

如何选择?

  • 新项目: 强烈建议使用 App Router。它是 Next.js 的未来,提供了更强大的功能、更好的性能潜力(特别是通过服务器组件和流式传输)和更现代的开发体验。
  • 现有项目: 如果项目稳定且维护良好,可以继续使用 Pages Router。Next.js 承诺会长期支持 Pages Router。也可以考虑逐步迁移到 App Router,两者可以在同一个项目中并存。
  • 团队熟悉度: App Router 的心智模型(尤其是服务器组件和客户端组件的区别)需要一定的学习曲线。如果团队对 Pages Router 非常熟悉且项目时间紧张,可以暂时维持现状。

App Router 代表了 Web 开发,特别是 React 生态系统的一个重要演进,它旨在简化全栈应用的开发,同时提供顶级的用户体验和性能。