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>; } fetchAPI 被扩展,自动处理缓存和重新验证 (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:
- 提供了新的
metadataAPI。 - 可以在
layout.js或page.js中导出静态metadata对象,或动态生成元数据的generateMetadata函数。 - 支持动态图片生成 (
generateImageMetadata) 和sitemap.xml,robots.txt等。
- 提供了新的
8. 缓存和重新验证:
Pages Router:
- 主要通过
getStaticProps的revalidate选项实现 ISR (Incremental Static Regeneration)。 getServerSideProps页面默认不缓存,除非配置 CDN。
- 主要通过
App Router:
fetchAPI 进行了扩展,支持更细粒度的缓存控制(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 生态系统的一个重要演进,它旨在简化全栈应用的开发,同时提供顶级的用户体验和性能。