# next源码主线:从请求到渲染

# 这篇怎么读

目标不是覆盖所有源码,而是建立“可追踪主线”:
一次 HTTP 请求进入 Next 后,如何路由、如何渲染、如何输出。

阅读目标建议定成三层:

  • 流程层:知道每一步做什么
  • 职责层:知道每层模块负责什么
  • 调试层:知道如何验证你的理解

# 总体链路(概念)

  1. Node/Edge 运行时接收请求
  2. 路由匹配(App Router segment)
  3. 组装渲染上下文(headers/cookies/params)
  4. 执行 Server Components
  5. 生成 HTML 与 RSC Payload
  6. 返回流式响应,客户端接管

如果用一句话概括:
Next 在服务端先渲染“可见内容”,再通过 Flight 把“组件语义”传给客户端恢复。


# 关键模块(阅读抓手)

下述是“职责级地图”,便于源码定位时不迷路。

  • 路由解析:负责把 URL 映射到 app 目录下的 segment 树
  • 渲染入口:创建 render context,并驱动 React 服务端渲染流程
  • Flight 序列化:把服务端组件树结果编码成客户端可恢复的数据流
  • 响应层:把 HTML 与 RSC 片段按流式方式输出

# 建议的源码入口顺序(避免迷路)

  1. 先看请求处理入口(dev/prod 各有分支)
  2. 再看 app-router 的渲染入口
  3. 然后看 Flight stream 的编码与输出
  4. 最后看客户端恢复链路(hydrate + router state)

不要一上来就追所有 util 文件,会陷入细节噪声。


# App Router 的核心理解

在 App Router 下,你看到的是目录树;Next 内部看到的是“segment 图 + 边界信息”:

  • layout:共享外壳
  • page:叶子内容
  • loading:异步边界占位
  • error:错误边界

这决定了为什么 Next 能做分段流式返回,而不是等整页算完。

# 对应到源码认知

你在目录里看到的是文件树,框架内部会把它构造成“可渲染树 + 边界元信息”:

  • 哪些边界可提前输出
  • 哪些边界需要等异步数据
  • 哪些边界失败后走 error/fallback

这正是 streaming 行为稳定可控的基础。


# 为什么要输出 RSC Payload

只输出 HTML 不够,因为客户端还要知道组件树结构与边界状态。
RSC Payload(Flight)承担了“让客户端恢复同构视图”的桥梁角色。

你可以理解为:

  • HTML:给浏览器立即可见内容
  • Flight:给 React 客户端恢复组件语义

# 深一点的理解

Flight 不是“再传一份 HTML”,而是传“组件引用 + props + 边界状态”的可恢复描述。
这让客户端不需要重跑整套服务端逻辑,也能接上同一棵 UI 语义树。


# Streaming 的工程价值

# 没有 streaming

后端必须等慢接口全部完成,TTFB 和首屏都被拖慢。

# 有 streaming

可先返回壳 + 已完成片段,慢块后续补齐。
用户“先见内容,再逐步完整”,体感明显更好。

# 关键取舍

  • streaming 提升感知速度,但也会增加调试复杂度
  • 边界划分太碎会增加心智成本
  • 边界划分太粗会回退到“整页等待”

资深实践里,通常按“首屏关键区 / 非关键区”切分 Suspense,而不是按组件颗粒度随意切。


# 请求链路中的缓存决策点

源码层你要重点关注“何时读取缓存、何时判定失效”:

  • 数据请求层(fetch cache)
  • 路由响应层(route segment cache)
  • 重验证触发点(time/tag/path)

理解这些点后,线上“为什么这页没更新”问题才有排查路径。


# 源码阅读建议(按场景)

建议从这三个场景切入追源码:

  1. 纯静态页面请求
  2. 含 fetch + revalidate 的页面请求
  3. 含 loading.tsx + Suspense 的流式页面请求

每个场景都回答 4 个问题:

  • 路由如何命中
  • 数据何时读取
  • 响应何时首字节输出
  • 客户端如何接管

# 再加 2 个问题(资深视角)

  • 缓存命中发生在哪一层
  • 出错后 fallback 与重试在哪个阶段触发

# 一个最小实验(建议你本地跑)

建一个页面里串两个请求:

  • 快请求(50ms)
  • 慢请求(1500ms,放在 Suspense 里)

然后抓包看响应时间线,你会直观看到流式分段输出。

建议同时打开:

  • 浏览器 Network(看分段返回)
  • Server 日志(看各数据源耗时)
  • React DevTools(看 hydration 边界)

把三者对齐,你对“框架行为”的理解会非常扎实。


# 调试技巧:把框架黑盒变白盒

# 1)打点策略

在关键数据函数、layout/page 入口、route handlers 统一加 requestId 打点。
同一个 requestId 串起服务端日志与前端 waterfall。

# 2)构造故障注入

主动注入:

  • 慢接口(sleep 2s)
  • 失败接口(500)
  • 缓存失效延迟

观察 loading/error 边界与响应分段是否符合预期。

# 3)做“冷/热缓存”对照

同一路由分别测:

  • 冷缓存首访
  • 热缓存复访

这能直接验证你的 revalidate/tag 方案是否真的生效。


# 案例:商品详情页的请求链路拆解

页面结构:

  • layout:站点壳
  • page:商品基本信息(可缓存)
  • Suspense A:价格库存(实时)
  • Suspense B:推荐商品(弱实时)

链路收益:

  • 首屏先回基础信息和骨架
  • 实时区块后续补齐
  • 推荐区块可高缓存命中

这种拆法在电商、内容社区、SaaS 控制台都适用。


# 小结

Next 源码阅读最怕“横向泛读”。
更有效的方法是:拿请求链路做主线,按场景纵向跟踪。
抓住“路由 -> RSC -> Streaming -> Hydration”四段,理解会非常稳定。