Appearance
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)。
- 服务器端逻辑通过
getServerSideProps
或getStaticProps
执行,其结果作为 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)。- 不再需要
getServerSideProps
或getStaticProps
。数据获取逻辑更贴近组件本身。 - 客户端组件中数据获取方式与 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.js
或page.js
中导出静态metadata
对象,或动态生成元数据的generateMetadata
函数。 - 支持动态图片生成 (
generateImageMetadata
) 和sitemap.xml
,robots.txt
等。
- 提供了新的
8. 缓存和重新验证:
Pages Router:
- 主要通过
getStaticProps
的revalidate
选项实现 ISR (Incremental Static Regeneration)。 getServerSideProps
页面默认不缓存,除非配置 CDN。
- 主要通过
App Router:
fetch
API 进行了扩展,支持更细粒度的缓存控制(cache
选项:force-cache
,no-store
等)和按需/基于时间的重新验证 (next: { revalidate: number }
或tags
)。- Route Segment Config: 可以在
page.js
或layout.js
中导出配置,如export const revalidate = 60;
。 - 提供了更灵活和强大的缓存策略。
核心区别总结:
特性 | Pages Router | App Router |
---|---|---|
目录 | pages | app |
组件默认 | 客户端组件 | 服务器组件 |
数据获取 | getServerSideProps , getStaticProps | 服务器组件内 async/await , 扩展的 fetch |
布局 | _app.js ,手动嵌套 | layout.js (持久化), template.js (重渲染), 自动嵌套 |
加载 UI | 手动管理 | loading.js (基于 Suspense) |
错误处理 | _error.js , 手动 Error Boundaries | error.js , global-error.js (自动 Error Boundaries) |
流式传输 | 不支持 | 支持 (Streaming HTML) |
API 路由 | pages/api | route.js (Route Handlers) |
SEO Meta | next/head | metadata 对象或 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 生态系统的一个重要演进,它旨在简化全栈应用的开发,同时提供顶级的用户体验和性能。