- 发布日期
第 01 讲|Next.js 是什么:从「React 框架」到「全栈运行时」
理解 Next.js 从「React 框架」到「全栈运行时」的定位演进,建立整体架构认知
阶段一:框架认知与源码导航 · 第 1 / 40 讲 难度:⭐⭐ · 预计耗时:1.5–2 小时
学习目标
- 能用一句话描述 Next.js 解决了 React 的哪 5 个痛点。
- 能说清 Pages Router 与 App Router 的差异、以及二者今天的取舍。
- 能在自己脑中画出 Next.js 的「五件套架构」(路由、渲染、构建、运行时、数据)。
- 跑通一个最小的 App Router demo,看到 RSC payload。
1. Next.js 到底是什么
React 官方文档里有一句话:"React 只是 UI 层"。但要把 React 变成一个生产可用的 Web 应用,你至少需要:
- 路由(routing):URL → 组件树。
- 渲染策略(SSR / SSG / ISR / RSC):什么时候渲染、在哪里渲染。
- 数据获取(data fetching / mutations):组件如何拿到数据、如何更新数据。
- 构建产物(bundling / code splitting / asset optimization):把代码变成可下发的 JS / CSS / 图像。
- 运行时(Node / Edge / Browser):在哪里执行那些"非 React"的代码(中间件、API、Image 优化等)。
Next.js 就是把这 5 件事打包成一个开箱即用的"全栈框架"——它不只是 React 的脚手架,而是一个完整的 HTTP 服务 + 构建器 + 客户端 runtime。
与 Remix / Nuxt / SvelteKit 等竞品相比,Next.js 当前最大的差异是:深度押注 React Server Components (RSC) 与 Partial Prerendering (PPR),把"哪些代码在服务器执行"作为框架的核心抽象。
2. Pages Router vs App Router:分水岭
Next.js 同时支持两套路由系统。
| 维度 | Pages Router (pages/) | App Router (app/) |
|---|---|---|
| 路由约定 | pages/[name].tsx 一个文件 = 一个路由 | app/[name]/page.tsx,文件夹 + 多个约定文件 |
| 默认组件类型 | 客户端组件(React 18 之前的模型) | Server Components |
| 数据获取 | getStaticProps / getServerSideProps / getStaticPaths | 直接在 Server Component 中 await fetch(...) |
| Layout | 全局 _app.tsx + 手动嵌套 | layout.tsx 文件嵌套、并行路由、intercepting route |
| Mutation | API Route + 客户端 fetch | Server Actions('use server') |
| 流式渲染 | 不支持 | Streaming SSR + RSC payload 流 |
| 缓存模型 | 路由级(ISR) | 4 层缓存(Request / Data / Full Route / Router) |
| 状态 | 稳定维护中 | 官方主推的未来方向 |
源码层面,二者甚至共用同一个 base server,但走不同的"route module":
import type { AppPageModule } from './route-modules/app-page/module'
import type { AppRouteModule } from './route-modules/app-route/module.compiled'
import type { ErrorModule } from './load-default-error-components'
import type { PagesModule } from './route-modules/pages/module.compiled'
route-modules/ 下有四种 module:app-page / app-route / pages / pages-api,分别对应 App Router 的页面、App Router 的 Route Handler、Pages Router 的页面、Pages Router 的 API。这是理解整个框架的"四象限"。
本课程默认聚焦 App Router——Pages Router 只在涉及历史包袱、迁移时提及。
3. Next.js 的「五件套架构」
把仓库源码摊开看,整个框架可以拆成 5 个层次:
┌───────────────────────────────────────────────────────┐
│ ① CLI / Entry next-dev / next-build / next-start
│ packages/next/src/cli/
├───────────────────────────────────────────────────────┤
│ ② Build webpack / SWC / Turbopack
│ packages/next/src/build/
├───────────────────────────────────────────────────────┤
│ ③ Server Runtime base-server / next-server / dev-server / edge-adapter
│ packages/next/src/server/
├───────────────────────────────────────────────────────┤
│ ④ Render Pipeline app-render / page-render / route-handler / RSC
│ packages/next/src/server/app-render/, route-modules/
├───────────────────────────────────────────────────────┤
│ ⑤ Client Runtime app-router / segment-cache / Link / Image / Script
│ packages/next/src/client/
└───────────────────────────────────────────────────────┘
这 5 层有非常清晰的"上下游"关系:
- CLI 层 解析 flag、决定走 dev / build / start 哪条路径;
- 构建层 把用户的
app/目录、依赖、内置 templates 编译出 3 套 bundle(client / server / edge); - 服务运行时 接管 HTTP,做路由匹配、缓存命中、调用渲染;
- 渲染管线 是 Next.js 真正的"心脏",跑 React Server Components、SSR、Streaming,最后吐出 HTML 流 + RSC payload;
- 客户端运行时 负责接住 RSC payload、做 segment cache、prefetch、hydration、SPA 导航。
这本系列后续 39 讲就是按这 5 层逐个展开。记住这张图,你以后看到任何 Next.js 问题,先问自己一句:这是哪一层的问题?
4. 一个最小 App Router 实验
我们生成一个最小 fixture,亲眼看到 RSC payload。
步骤 1:生成 fixture
按 AGENTS.md 的约定,使用仓库内的脚手架命令:
pnpm new-test -- --args true lecture-01 e2e
这会在 test/e2e/lecture-01/ 下生成最小 App Router 模板(app/page.tsx + 测试文件)。
步骤 2:直接启动 Next.js 看效果
为了避开 Jest 测试框架的开销,直接用 dist 里的 next 命令跑这个目录:
# 先确保 packages/next 已构建
pnpm --filter=next build
# 在 fixture 目录起 dev server
cd test/e2e/lecture-01
node ../../../packages/next/dist/bin/next dev --port 3000
步骤 3:抓 HTML 与 RSC payload
打开第二个终端:
# 抓首屏 HTML(注意末尾 self.__next_f.push 的脚本块)
curl -s http://localhost:3000/ | head -200
# 抓 RSC payload(同一个 URL,加 _rsc 参数 + RSC header)
curl -s -H 'RSC: 1' 'http://localhost:3000/?_rsc=1' | head -50
你会看到两类完全不同的输出:
- 第一个返回 HTML + 内嵌的
self.__next_f.push([1, "..."]),这是 SSR 的产物,里面已经包含了 RSC payload 的内联拷贝。 - 第二个返回 纯 RSC payload(看起来像
0:["$","$L1",null,{...}]这种行式格式),这是浏览器在 SPA 导航时拉取的格式。
这就是 App Router 与传统 SSR 框架的根本差异:HTML 不再是唯一的"传输介质",RSC payload 才是 Next.js 的"原始数据流"。后续第 17 讲会拆开这个 payload 协议。
5. 重难点
5.1 不要拿 Pages 思维理解 App
- App Router 里
page.tsx默认是 Server Component,你 import React Context、用useState都会立刻报错。 - "我以前用
getServerSideProps拿数据"——在 App Router 里直接const data = await fetch(...),省略中间层。 - "我以前用
_app.tsx包 Provider"——在 App Router 里用app/layout.tsx,并把 Provider 抽成'use client'子组件。
5.2 "服务器组件"不等于"在服务器渲染"
- Server Component 在
next build/next dev期间也会运行——但不会被打到 client bundle 里。 - 一个
'use client'组件在第一次访问页面时也会在服务器跑一次(SSR),只是后续 hydration 后才在浏览器接管。 - 这一点是很多 hydration mismatch、
window is not defined报错的根因,第 7 讲展开。
5.3 框架边界 ≠ 业务边界
学 Next.js 时最大的陷阱是把"业务怎么写"和"框架怎么实现"混在一起。本课程会严格区分:
- 使用层(API 怎么调):只到第 14 讲为止还会大量提;
- 框架层(源码怎么跑):第 15 讲开始会以源码为主,不再举业务示例。
6. 检验问题
学完本讲,你应当能口头回答:
- Next.js 的"五件套架构"是什么?分别落在
packages/next/src/的哪些子目录? - 为什么 App Router 默认是 Server Component?这与 React 18 / RSC 提案是什么关系?
pages/与app/共存时,路由冲突怎么处理?(提示:看route-matcher-managers/)- 抓一个
?_rsc=1请求,能不能描述 RSC payload 的第一行格式? 'use client'在编译期与运行期分别做了什么?(这一题留到第 7 讲再深入)
7. 延伸阅读
- 官方文档:
docs/01-app/01-getting-started/index.mdx起的整个 App Router 入门系列。 - React Server Components 提案:react.dev 上的 Server Components RFC(基础理论)。
- 仓库
AGENTS.md顶部「Codebase structure」部分(必读)。
下一讲预告
第 02 讲|Monorepo 结构与发布包关系:我们会把 packages/、turbopack/、crates/ 全部展开,建立"我要修这个 bug,应该改哪个包"的肌肉记忆。