Next.js 工作流程(SSR 核心场景)
本文基于 React 服务端渲染(SSR)核心逻辑,以 Next.js 12 及之前的 Pages Router(带 getServerSideProps)为典型场景,同时补充 App Router 流式渲染特性,覆盖请求接收 → 服务端处理 → 客户端响应 → 水合交互全链路。
一、 整体流程概览
用户请求 → 服务端路由解析与数据预取 → 服务端组件渲染生成 HTML → 服务端返回资源包 → 浏览器首屏渲染 → 客户端 JS 下载与水合 → 客户端接管交互
二、 分步详细拆解
1. 用户发起请求
用户在浏览器输入 URL 或点击页面内链接,向 Next.js 服务器(基于 Node.js)发送 HTTP 请求,请求携带 URL 路径、Cookie、请求头等信息。
2. 服务端接收请求与路由解析
- Next.js 服务器监听端口并接收请求,通过内置路由系统匹配对应页面组件:
Pages Router:根据pages目录结构匹配路由(如pages/user/[id].js匹配/user/1)。App Router:根据app目录下的文件夹结构和page.js文件匹配路由。
- 路由匹配成功后,判断页面类型:若为 SSR 页面(
Pages Router导出getServerSideProps,App Router使用async组件或generateServerSideProps),进入服务端数据预取与渲染流程;若为静态生成(SSG)页面,直接返回预构建的 HTML。
3. 服务端数据预取(SSR 核心步骤)
该步骤仅在服务端执行,目的是获取页面渲染所需的完整数据,避免客户端二次请求导致首屏延迟:
Pages Router:执行页面导出的getServerSideProps方法,该方法接收context参数(包含路由参数、请求头、Cookie 等),可发起 API 请求、查询数据库获取数据,最终返回{ props: {...} },数据会作为组件属性传入页面组件。App Router:在page.js或layout.js中编写async函数直接获取数据,或使用generateServerSideProps方法,数据通过props传递给组件。- 关键特性:数据预取逻辑不会在客户端执行,完全隔离在服务端环境。
4. 服务端组件渲染生成 HTML
- Next.js 调用 React 服务端渲染引擎(
ReactDOMServer.renderToString或renderToPipeableStream),将页面组件与预取数据结合,生成带完整业务内容的静态 DOM 字符串。 - 组件渲染时的 Hooks 执行规则:
- 执行
useStateuseMemouseContext等无副作用 Hooks,用于计算组件状态与渲染内容。 - 完全跳过
useEffectuseLayoutEffect,这两个 Hooks 仅在客户端水合阶段执行。
- 执行
App Router流式渲染差异:服务端不一次性生成完整 HTML,而是分块返回 HTML 片段,浏览器可边接收、边渲染,提升首屏加载速度。
5. 服务端打包并返回资源
服务端将以下资源整合为 HTTP 响应,返回给浏览器:
- 完整 HTML 字符串:包含基础骨架(
<html><head><body>)、组件渲染的静态 DOM(嵌入<body>)、内联的初始数据(挂载到全局变量window.__NEXT_DATA__,包含预取数据、路由信息等)。 - 客户端 JS 脚本链接:包括 Next.js 运行时、React 核心库、页面专属组件逻辑代码,通过
<script>标签引入。 - 静态资源链接:CSS 文件(通过
<link>引入)、图片/字体等静态资源的 CDN 链接,支持代码分割与缓存优化。 - 辅助资源(可选):polyfill 脚本(兼容低版本浏览器)、埋点/监控脚本等。
- 核心注意点:不传输 React Fiber 树,Fiber 树是服务端渲染的临时内部数据结构,生成 HTML 后即被销毁。
6. 浏览器首屏渲染
浏览器接收响应后,立即解析 HTML 并渲染 <body> 中的静态 DOM,用户无需等待 JS 下载即可看到页面内容,这是 SSR 提升首屏体验的核心优势。同时,浏览器并行下载 HTML 中引用的 JS、CSS、图片等资源。
7. 客户端 JS 下载与水合(Hydration)
- JS 脚本下载完成后执行,Next.js 启动客户端水合流程,核心目标是将静态 DOM 转为可交互的 React 组件实例。
- 水合过程步骤:
- 读取
window.__NEXT_DATA__中的初始数据,避免重复请求。 - React 重新执行页面组件代码,构建客户端虚拟 DOM 与 Fiber 树。
- 对比客户端虚拟 DOM 与页面静态 DOM 的结构一致性,确保无差异后,为 DOM 节点绑定事件监听(如点击、输入)。
- 执行
useEffectuseLayoutEffect钩子,处理客户端副作用(如操作 DOM、发起非首屏数据请求、初始化第三方 SDK)。
- 读取
8. 客户端接管后续交互
水合完成后,页面进入完全可交互状态,后续操作由客户端 React 接管:
- 路由跳转:使用 Next.js 内置的
next/link或next/router实现客户端路由切换,无需重新请求服务端(除非跳转至新的 SSR 页面)。 - 状态更新:组件的
setStateuseReducer等操作触发客户端重新渲染,遵循 React 客户端渲染逻辑。 - 数据更新:非首屏数据请求(如分页加载、筛选条件变更)通过
useEffect在客户端发起,更新页面内容。
三、 关键特性补充
- 环境隔离:服务端代码(如
getServerSideProps)与客户端代码完全隔离,服务端可访问 Node.js API,客户端可访问浏览器 API(需在useEffect中使用)。 - 缓存策略:支持多种缓存方案,如 SSG 页面的构建时缓存、ISR(增量静态再生)的按需缓存、SSR 页面的请求级缓存,可通过配置灵活控制。
- 代码分割:Next.js 自动对 JS、CSS 进行代码分割,只加载当前页面所需资源,减少首屏加载体积。
四、 不同渲染模式的流程差异
| 渲染模式 | 核心差异点 | 适用场景 |
|---|---|---|
| SSR(服务端渲染) | 每次请求都执行数据预取与组件渲染 | 动态数据页面(如用户中心、实时榜单) |
| SSG(静态生成) | 构建时完成数据预取与 HTML 生成,请求时直接返回 | 静态内容页面(如博客、文档、营销页) |
| ISR(增量静态再生) | 基于 SSG,定时或按需重新生成页面 | 半静态半动态页面(如商品详情页) |
| 客户端渲染 | 服务端返回空 HTML,客户端 JS 加载后获取数据并渲染 | 后台管理系统等对 SEO 无要求的页面 |