# Next.js SSR/SSG路由与渲染模式深度解析 **版本说明**本文基于 Next.js 14.x 和 15.x 最新版本编写源码路径参考 packages/next/src/ 核心模块 --- ## 目录 1. [引言渲染模式的演进](#1-引言渲染模式的演进) 2. [Next.js 路由系统架构](#2-nextjs-路由系统架构) 3. [SSR服务器端渲染深度解析](#3-ssr服务器端渲染深度解析) 4. [SSG静态站点生成深度解析](#4-ssg静态站点生成深度解析) 5. [ISR增量静态再生成混合模式](#5-isr增量静态再生成混合模式) 6. [渲染模式选择指南](#6-渲染模式选择指南) 7. [性能优化实战](#7-性能优化实战) 8. [总结](#8-总结) --- ## 1. 引言渲染模式的演进 ### 1.1 Web 渲染简史 从传统的服务端渲染PHP、JSP到客户端渲染SPA再到现代的混合渲染模式Web 开发经历了一个螺旋式上升的过程。 mermaid timeline title Web 渲染技术演进史 1990s : 服务端渲染 (PHP/JSP/ASP)每次请求服务器生成完整HTML 2000s : AJAX 出现局部动态更新,用户体验提升 2010s : 单页应用 (SPA)React/Vue/Angular,完全客户端渲染 2015s : 同构渲染 (Universal SSR)React SSR,首次服务器渲染,后续客户端接管 2019s : 混合渲染时代 (Next.js)SSR/SSG/ISR 按需组合 2024s : 边缘渲染与部分渲染RSC/Streaming/PPR ### 1.2 Next.js 的核心价值 Next.js 将多种渲染模式统一到一个框架中让开发者可以在**页面级别**选择最适合的渲染策略 | 渲染模式 | 全称 | 适用场景 | SEO友好 | 首屏速度 | |---------|------|---------|---------|----------| | **SSR** | Server-Side Rendering | 动态内容、个性化页面 | ✅ 优秀 | ⚡ 中等 | | **SSG** | Static Site Generation | 营销页面、文档、博客 | ✅ 完美 | 最快 | | **ISR** | Incremental Static Regeneration | 周期性更新内容 | ✅ 优秀 | 快 | | **CSR** | Client-Side Rendering | 高交互应用、管理后台 | ❌ 较差 | 慢 | --- ## 2. Next.js 路由系统架构 ### 2.1 路由系统演进 Next.js 14.x 引入了 App Router基于 React Server Components与 Pages Router 共存。 mermaid graph TB A[Next.js 应用] -- B[Pages Routersrc/pages/] A -- C[App Routersrc/app/] B -- B1[文件系统路由] B -- B2[getServerSidePropsSSR] B -- B3[getStaticPropsSSG/ISR] C -- C1[嵌套布局] C -- C2[Server Components默认] C -- C3[Client Componentsuse client] C -- C4[数据获取方法异步组件] style B fill:#e1f5ff style C fill:#fff4e1 style C2 fill:#c8e6c9 ### 2.2 文件路由映射规则 **App Router 路由示例**Next.js 14 src/app/ ├── (marketing)/ # 路由组不影响URL │ ├── about/ │ │ └── page.tsx → /about │ └── layout.tsx # 共享布局 ├── blog/ │ ├── [slug]/ # 动态路由 │ │ └── page.tsx → /blog/post-1 │ └── page.tsx → /blog ├── shop/ │ ├── [[...slug]]/ # 捕获所有路由可选 │ │ └── page.tsx → /shop, /shop/a, /shop/a/b │ └── [...slug]/ # 捕获所有路由必需 │ └── page.tsx → /shop/a, /shop/a/b (非/shop) └── page.tsx → / **核心源码位置**Next.js 14.2.x - 路由匹配逻辑packages/next/src/server/app-render/app-render.tsx - 文件系统路由解析packages/next/src/server/dev/parse-component-info.ts --- ## 3. SSR服务器端渲染深度解析 ### 3.1 SSR 工作原理 服务器在**每次请求时**动态生成 HTML然后发送给客户端。 mermaid sequenceDiagram participant User as 用户浏览器 participant Server as ️ Next.js 服务器 participant Data as ️ 数据库/API User-Server: 1. 请求页面 Server-Data: 2. 获取数据每次都请求 Data--Server: 3. 返回最新数据 Server-Server: 4. 渲染 React 组件 → HTML Server--User: 5. 返回完整 HTML User-User: 6. 显示内容首屏快 User-User: 7. 加载 JShydrate可交互 ### 3.2 Pages Router 中的 SSR 实现 使用 getServerSideProps 在每次请求时获取数据 typescript // src/pages/product/[id].tsx import { GetServerSideProps, GetServerSidePropsContext } from next // 产品数据类型定义 interface Product { id: string name: string price: number description: string lastUpdated: string // 展示实时性 } interface ProductPageProps { product: Product timestamp: string // 服务器渲染时间 } /** * 在每次请求时在服务器端执行 * * 源码参考 * - packages/next/src/server/render.tsx (renderToHTML) * - packages/next/src/server/get-server-side-props.ts */ export const getServerSideProps: GetServerSideProps async ( context: GetServerSidePropsContext ) { const { id } context.params || {} try { // 实时获取产品数据包含库存、价格变动 const response await fetch(https://api.example.com/products/${id}, { headers: { // 可传递用户 Cookie 进行个性化请求 cookie: context.req.headers.cookie || } }) if (!response.ok) { return { notFound: true // 返回 404 页面 } } const product: Product await response.json() // 可以访问请求上下文req/res、cookies、query 参数 return { props: { product, timestamp: new Date().toISOString() // 每次请求都会变化 }, // 可选设置 HTTP 缓存头CDN 缓存但 Next.js 仍会重新渲染 // headers: { // Cache-Control: public, s-maxage60, stale-while-revalidate30 // } } } catch (error) { return { redirect: { destination: /error, // 出错时重定向 permanent: false } } } } // React 组件接收 props 进行渲染 export default function ProductPage({ product, timestamp }: ProductPageProps) { return ({product.name}价格¥{product.price}{product.description}服务器渲染时间{new Date(timestamp).toLocaleString(zh-CN)}) } ### 3.3 App Router 中的 SSR 实现 在 App Router 中SSR 是**默认行为**Server Components typescript // src/app/product/[id]/page.tsx // 文件即路由无需额外配置 import { notFound } from next/navigation // 定义产品类型 interface Product { id: string name: string price: number stock: number } // 异步服务器组件默认就是 SSR // // 核心原理 // - packages/next/src/server/app-render/app-render.tsx // - renderToHTML() 将 React Server Components 流式渲染为 HTML export default async function ProductPage({ params }: { params: Promise{ id: string } }) { // 在服务器端执行每次请求都会运行 const { id } await params // 直接 fetch 数据自动去重、缓存可配置 const res await fetch(https://api.example.com/products/${id}, { // 默认缓存策略可配置 // next: { revalidate: 0 } // 禁用缓存纯 SSR }) if (!res.ok) { notFound() // 调用 notFound() 显示 404 页面 } const product: Product await res.json() // 返回 JSX在服务器端序列化为 HTML return ({product.name}价格¥{product.price}库存{product.stock} 件渲染时间{new Date().toLocaleString(zh-CN)}) } ### 3.4 SSR 核心源码分析 **渲染流程关键代码**Next.js 14.2.x typescript // packages/next/src/server/render.tsx (简化版) import { renderToReadableStream } from react-dom/server /** * SSR 核心渲染函数 * * 关键步骤 * 1. 创建 React 组件树 * 2. 调用 getServerSideProps 或异步组件获取数据 * 3. 使用 renderToReadableStream 将组件流式渲染为 HTML * 4. 返回完整 HTML hydration 数据 */ export async function renderToHTML({ pathname, query, req, res }: RenderOpts): Promise { // 1. 数据获取阶段 const props await getServerSideProps({ req, res, query }) // 2. 渲染阶段流式渲染 const stream await renderToReadableStream( , { // 启用 Suspense 流式渲染 onError(error) { console.error(SSR Error:, error) } } ) // 3. 等待流完成 await stream.allReady // 4. 序列化 hydration 数据嵌入到 HTML 中 const hydrationData JSON.stringify(props) return { html: stream, hydrationData // 传递给客户端进行 hydrate } } ### 3.5 SSR 优缺点对比 | 维度 | 优势 | 劣势 | |-----|------|------| | **SEO** | ✅ 完美爬虫直接获取完整 HTML | - | | **首屏速度** | ⚡ 快服务器已渲染好 | 受服务器响应时间影响 | | **数据新鲜度** | 每次请求都最新 | - | | **服务器负载** | - | 高每次请求都计算 | | **缓存难度** | - | ❌ 难个性化内容无法 CDN 缓存 | | **开发复杂度** | ⭐ 中等 | - | --- ## 4. SSG静态站点生成深度解析 ### 4.1 SSG 工作原理 **构建时**Build Time预先生成静态 HTML 文件部署后直接返回静态文件。 mermaid sequenceDiagram participant Dev as ‍ 开发者 participant Build as Next.js 构建 participant Server as ️ 生产服务器 participant User as 用户 Dev-Build: 1. 运行 next build Build-Build: 2. 执行 getStaticProps Build-Build: 3. 生成所有路径的 HTML Build-Server: 4. 部署静态文件 Note over Server,User: 生产环境 User-Server: 5. 请求页面 Server--User: 6. 立即返回静态 HTML毫秒级 ### 4.2 Pages Router 中的 SSG 使用 getStaticProps getStaticPaths 实现静态生成 typescript // src/pages/blog/[slug].tsx import { GetStaticProps, GetStaticPaths, GetStaticPropsContext } from next interface BlogPost { id: string title: string content: string author: string publishedAt: string } interface BlogPageProps { post: BlogPost } /** * 构建时生成静态页面 * * 源码参考 * - packages/next/src/server/get-static-props.ts * - packages/next/src/build.ts (generateStaticPages) */ export const getStaticProps: GetStaticProps async ( context: GetStaticPropsContext ) { const { slug } context.params || {} // 构建时获取文章数据只执行一次 const res await fetch(https://api.example.com/blog/${slug}) const post: BlogPost await res.json() return { props: { post }, // 可选启用 ISR增量静态再生成 revalidate: 60 // 每 60 秒允许重新生成一次 } } /** * 指定哪些路径需要预渲染 * * 构建时会为每个 path 调用 getStaticProps 生成 HTML */ export const getStaticPaths: GetStaticPaths async () { // 构建时获取所有文章列表 const res await fetch(https://api.example.com/blog) const posts: BlogPost[] await res.json() // 返回需要预渲染的路径列表 const paths posts.map((post) ({ params: { slug: post.id } })) return { paths, fallback: false // false只渲染这些路径, 404true首次访问时生成 } } export default function BlogPage({ post }: BlogPageProps) { return ({post.title}作者{post.author}发布于{post.publishedAt}{post.content}