Next.js SSR/SSG:路由与渲染模式深度解析
2026/4/24 0:09:57 网站建设 项目流程
# 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 Router
src/pages/] A --> C[App Router
src/app/] B --> B1[文件系统路由] B --> B2[getServerSideProps
SSR] B --> B3[getStaticProps
SSG/ISR] C --> C1[嵌套布局] C --> C2[Server Components
默认] C --> C3[Client Components
use 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. 加载 JS,hydrate(可交互) ``` ### 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-maxage=60, stale-while-revalidate=30' // } } } 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=只渲染这些路径, 404=true=首次访问时生成 } } export default function BlogPage({ post }: BlogPageProps) { return (

{post.title}

作者:{post.author}

{post.content}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询