Next.js 13/14 引入的 app 目录 (App Router) 和 pages 目录 (Pages Router) 有什么本质区别?
Next.js 13/14 引入的 App Router (app 目录) 与传统的 Pages Router (pages 目录) 不仅仅是目录名称的变化,而是底层架构和思维模式的彻底重构。
核心区别在于:App Router 是建立在 React Server Components (RSC) 之上的,而 Pages Router 是建立在传统的 SSR/SSG 模式之上的。
以下是它们在六个维度的本质区别:
1. 渲染范式:默认服务端组件 (RSC) vs 默认客户端组件
这是最大的本质区别。
Pages Router (
pages):- 默认全是客户端组件。虽然它支持 SSR(服务端渲染 HTML),但浏览器加载后,所有组件都会经历“水合(Hydration)”过程,React 会在客户端重新执行代码以接管页面。
- 发送给浏览器的 JavaScript 包(Bundle)较大。
App Router (
app):- 默认全是服务端组件 (Server Components)。这些组件在服务器上渲染成特殊的序列化格式,不会打包进客户端的 JavaScript Bundle 中。
- 零 Bundle Size:如果一个组件不需要交互(如纯展示的文本、布局),它在客户端不需要任何 JS 代码。
- 需要交互(useState, useEffect)时,必须显式在文件顶部声明
'use client'。
2. 路由与文件系统:文件夹即路由 vs 文件即路由
Pages Router:
- 文件即路由。
pages/about.js直接对应/about。 - 逻辑比较扁平,难以表达复杂的嵌套关系。
- 文件即路由。
App Router:
- 文件夹即路由。
app/about/page.js对应/about。 - 引入了特殊文件约定:每个文件夹下可以包含
layout.js(布局),page.js(页面 UI),loading.js(加载状态),error.js(错误处理),not-found.js(404),route.js(API 端点)。 - 这种结构让 UI 的各个状态(加载中、报错、布局)与路由逻辑紧密绑定。
- 文件夹即路由。
3. 数据获取:Fetch API vs 特殊钩子函数
Pages Router:
- 依赖特定的 Next.js 函数:
getServerSideProps(SSR),getStaticProps(SSG),getInitialProps(Legacy)。 - 数据获取必须在页面顶层进行,不能在组件内部直接获取数据,导致“Props Drilling”(层层传递数据)问题严重。
- 依赖特定的 Next.js 函数:
App Router:
- 废弃了
getStaticProps等函数。 - 直接在 Server Component 内部使用标准的
async/await和fetch。 - 支持组件级数据获取:你可以在 Layout 中获取导航栏数据,在 Page 中获取内容数据,互不干扰,并行请求。
- 通过
fetch的配置项控制缓存(例如cache: 'no-store'等同于 SSR,force-cache等同于 SSG)。
- 废弃了
4. 布局系统 (Layouts):嵌套与状态保持
Pages Router:
- 原生不支持嵌套布局。通常需要使用
_app.js或者在页面组件上挂载getLayout属性这种“黑客”手段来实现。 - 路由切换时,如果布局逻辑处理不好,整个页面容易重新渲染,导致状态丢失(例如滚动条位置、输入框内容)。
- 原生不支持嵌套布局。通常需要使用
App Router:
- 原生支持嵌套布局。
app/dashboard/layout.js可以包裹app/dashboard/settings/page.js。 - 状态保持:当在同一布局下的不同页面间导航时(例如从
/dashboard/settings到/dashboard/analytics),父级布局dashboard/layout.js不会重新渲染,其内部状态(如搜索框输入、侧边栏折叠状态)会被完全保留。
- 原生支持嵌套布局。
5. 流式传输 (Streaming) 与 Suspense
Pages Router:
- 阻塞式渲染。服务器必须等待所有数据(
getServerSideProps)准备好,才能生成 HTML 发送给浏览器。用户在数据加载完成前看到的是白屏。
- 阻塞式渲染。服务器必须等待所有数据(
App Router:
- 原生支持 React Suspense 和 Streaming。
- 服务器可以先发送静态的 HTML shell(如导航栏、页脚),数据加载慢的部分显示 Loading 骨架屏(通过
loading.js或<Suspense>),等数据准备好后再通过流式传输补全内容。 - 这显著提高了 TTFB (Time to First Byte) 和 FCP (First Contentful Paint)。
6. 缓存机制 (Caching)
Pages Router:
- 缓存主要依赖 ISR (Incremental Static Regeneration) 配置,粒度较粗。
App Router:
- 引入了多级缓存系统:
- Request Memoization: 同一次渲染中重复的 fetch 请求会自动去重。
- Data Cache: 跨请求的数据缓存(类似服务端的 CDN)。
- Full Route Cache: 静态路由的 HTML 和 RSC Payload 缓存。
- Router Cache: 客户端内存缓存,通过 Prefetch 实现瞬间导航。
- 引入了多级缓存系统:
总结对比表
| 特性 | Pages Router (pages) |
App Router (app) |
|---|---|---|
| 核心架构 | 传统 SSR/SSG (组件默认需水合) | React Server Components (默认服务端) |
| 路由定义 | 文件路径即 URL | 文件夹路径即 URL,page.js 定义 UI |
| 数据获取 | getServerSideProps / getStaticProps |
Async Components + fetch API |
| 布局 (Layout) | 困难,需使用 _app.js 或模式 |
原生支持嵌套,切换页面保留状态 |
| 加载体验 | 全页面阻塞,等待数据 | Streaming (流式),渐进式加载 UI |
| 客户端 JS 大小 | 较大 (所有组件逻辑都下载) | 极小 (仅下载 'use client' 组件) |
| SEO | 优秀 | 优秀 (且元数据 API 更现代化) |
结论:该选哪个?
- 新项目:强烈建议使用 App Router。它是 Next.js 的未来,React 官方团队也在向 RSC 方向发展。虽然学习曲线稍陡峭,但能带来更好的性能和开发体验。
- 老项目:如果没有痛点(如复杂的嵌套布局、极致的性能优化需求),可以继续使用 Pages Router。Next.js 会长期支持两者共存。