1. 先建立一个核心认知
前端常说的 CSR、SSR、SSG、ISR,本质上都在回答同一个问题:
用户访问一个页面时,页面 HTML 是在什么时候、在哪里生成的?
不同答案会影响:
- 首屏加载速度
- SEO 效果
- 服务器压力
- 页面数据实时性
- 部署复杂度
- 前端工程结构
- 用户交互体验
可以先用一句话记住:
| 模式 | 一句话理解 |
|---|---|
| CSR | 浏览器拿到空壳 HTML 后,用 JS 在客户端渲染页面 |
| SSR | 每次请求时,服务端实时渲染 HTML 后返回 |
| SSG | 构建项目时提前生成静态 HTML |
| ISR | 先静态生成,后续按时间或触发条件增量更新静态页面 |
这些不是互斥关系。现代框架通常支持混合使用,比如同一个 Next.js 项目里,首页可以 SSG,后台页面可以 CSR,详情页可以 ISR,强实时页面可以 SSR。
2. CSR:Client-Side Rendering,客户端渲染
2.1 CSR 是什么
CSR 是客户端渲染。服务器通常返回一个很简单的 HTML,例如:
<!doctype html>
<html>
<head>
<title>CSR App</title>
</head>
<body>
<div id="app"></div>
<script src="/assets/main.js"></script>
</body>
</html>
真正的页面内容不在 HTML 里,而是在浏览器下载并执行 JavaScript 后,由 Vue、React 等框架生成。
Vue 典型入口:
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
React 典型入口:
// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
2.2 CSR 的请求过程
- 浏览器请求页面。
- 服务器返回 HTML 空壳。
- 浏览器下载 JS、CSS。
- JS 执行,框架开始渲染页面。
- 页面组件请求接口数据。
- 数据返回后,页面更新。
2.3 CSR 的优点
- 前后端分离清晰,开发体验好。
- 页面跳转通常很流畅,适合复杂交互。
- 服务端压力小,主要提供静态资源和 API。
- 非常适合管理后台、控制台、工作台、编辑器类应用。
2.4 CSR 的缺点
- 首屏依赖 JS 下载和执行,白屏时间可能较长。
- SEO 不友好,爬虫如果不执行 JS,拿不到真实内容。
- 低端设备上 JS 执行慢,体验会下降。
- 首屏接口请求通常发生在浏览器端,链路更长。
2.5 CSR 适用场景
适合:
- 后台管理系统
- SaaS 控制台
- 数据看板
- 在线编辑器
- 内部系统
- 登录后才能访问的业务页面
- SEO 不重要、交互复杂的应用
不太适合:
- 内容站
- 新闻站
- 商品详情页
- 博客文章页
- 依赖搜索引擎收录的营销页
2.6 CSR 示例:Vue 页面请求数据
<script setup lang="ts">
import { onMounted, ref } from 'vue'
interface Article {
id: number
title: string
summary: string
}
const loading = ref(false)
const articles = ref<Article[]>([])
onMounted(async () => {
loading.value = true
try {
const response = await fetch('/api/articles')
articles.value = await response.json()
} finally {
loading.value = false
}
})
</script>
<template>
<section>
<p v-if="loading">加载中...</p>
<article v-for="article in articles" :key="article.id">
<h2>{{ article.title }}</h2>
<p>{{ article.summary }}</p>
</article>
</section>
</template>
这个页面的内容是浏览器运行 JS 后才出现的,这就是典型 CSR。
3. SSR:Server-Side Rendering,服务端渲染
3.1 SSR 是什么
SSR 是服务端渲染。用户每次请求页面时,服务端会先运行前端组件代码,把页面渲染成 HTML 字符串,再返回给浏览器。
浏览器一开始拿到的 HTML 里就有真实内容,例如:
<main>
<h1>文章标题</h1>
<p>文章摘要内容...</p>
</main>
之后浏览器再下载 JS,并进行 hydration(水合),让静态 HTML 变成可交互页面。
3.2 Hydration 是什么
Hydration 通常翻译为“水合”。
SSR 返回的 HTML 虽然已经能展示内容,但按钮点击、状态更新、路由切换等交互还不能工作。浏览器下载 JS 后,框架会把事件、状态、组件逻辑绑定到已有 HTML 上,这个过程就是 hydration。
简单理解:
服务端返回可见 HTML
+
浏览器加载 JS 并绑定交互
=
完整可交互页面
3.3 SSR 的请求过程
- 浏览器请求页面。
- 服务端执行页面组件逻辑。
- 服务端请求必要数据。
- 服务端生成完整 HTML。
- 浏览器收到 HTML,立即展示内容。
- 浏览器下载 JS。
- 框架进行 hydration。
- 页面变得可交互。
3.4 SSR 的优点
- 首屏内容出现更快。
- SEO 友好,爬虫能直接读取 HTML 内容。
- 适合弱网环境下优先展示内容。
- 首屏数据可以在服务端聚合,减少浏览器端等待。
3.5 SSR 的缺点
- 服务端压力更大,因为每次请求都要渲染。
- 架构复杂度更高,需要同时考虑服务端和浏览器环境。
- 代码里不能随意使用
window、document等浏览器专属对象。 - 容易出现 hydration mismatch(水合不一致)。
3.6 SSR 适用场景
适合:
- 电商详情页
- 新闻详情页
- SEO 重要的内容页
- 首屏性能要求高的页面
- 需要根据用户请求实时生成内容的页面
- 价格、库存、权限等强实时页面
不太适合:
- 完全登录后的后台系统
- 高度复杂但 SEO 不重要的交互页面
- 静态内容很多、更新不频繁的站点
3.7 Next.js SSR 示例
Next.js App Router 中,如果页面使用服务端数据请求,默认就可以在服务端渲染。
// app/articles/[id]/page.tsx
interface ArticlePageProps {
params: {
id: string
}
}
async function getArticle(id: string) {
const response = await fetch(`https://example.com/api/articles/${id}`, {
cache: 'no-store'
})
if (!response.ok) {
throw new Error('文章加载失败')
}
return response.json()
}
export default async function ArticlePage({ params }: ArticlePageProps) {
const article = await getArticle(params.id)
return (
<main>
<h1>{article.title}</h1>
<p>{article.summary}</p>
<article>{article.content}</article>
</main>
)
}
关键点:
cache: 'no-store'
这表示每次请求都重新获取数据,更接近 SSR 的实时渲染。
3.8 Nuxt SSR 示例
Nuxt 默认支持 SSR。
<!-- pages/articles/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: article } = await useFetch(`/api/articles/${route.params.id}`)
</script>
<template>
<main v-if="article">
<h1>{{ article.title }}</h1>
<p>{{ article.summary }}</p>
<article>{{ article.content }}</article>
</main>
</template>
服务端会先执行 useFetch,拿到数据后再渲染页面 HTML。
4. SSG:Static Site Generation,静态站点生成
4.1 SSG 是什么
SSG 是静态生成。页面不是用户请求时才生成,而是在项目构建时提前生成 HTML 文件。
例如构建后可能得到:
dist/
index.html
about/index.html
articles/1/index.html
articles/2/index.html
用户访问时,服务器或 CDN 直接返回这些静态 HTML 文件。
4.2 SSG 的请求过程
构建阶段:
- 执行构建命令。
- 框架读取数据源。
- 为每个页面生成 HTML。
- 把 HTML、CSS、JS 输出到静态目录。
访问阶段:
- 浏览器请求页面。
- CDN 或静态服务器直接返回 HTML。
- 浏览器展示页面。
- JS 加载后进行交互增强。
4.3 SSG 的优点
- 访问速度非常快。
- 可以直接部署到 CDN。
- 服务端成本低。
- 安全性较高,因为不需要运行复杂服务端逻辑。
- SEO 友好。
4.4 SSG 的缺点
- 数据更新后通常需要重新构建。
- 页面数量极多时,构建时间会变长。
- 不适合每次访问都需要实时数据的页面。
4.5 SSG 适用场景
适合:
- 文档站
- 博客
- 官网
- 营销页
- 帮助中心
- 更新频率低的内容页面
- 可以接受分钟级或发布级延迟的数据页面
不太适合:
- 股票行情
- 实时库存
- 实时搜索结果
- 用户个性化页面
- 强权限页面
4.6 Next.js SSG 示例
如果使用 generateStaticParams,Next.js 可以在构建时生成静态页面。
// app/articles/[id]/page.tsx
interface ArticlePageProps {
params: {
id: string
}
}
async function getArticles() {
const response = await fetch('https://example.com/api/articles')
return response.json()
}
async function getArticle(id: string) {
const response = await fetch(`https://example.com/api/articles/${id}`)
return response.json()
}
export async function generateStaticParams() {
const articles = await getArticles()
return articles.map((article: { id: number }) => ({
id: String(article.id)
}))
}
export default async function ArticlePage({ params }: ArticlePageProps) {
const article = await getArticle(params.id)
return (
<main>
<h1>{article.title}</h1>
<p>{article.summary}</p>
</main>
)
}
构建时,框架会根据 generateStaticParams 返回的 id 列表提前生成页面。
4.7 Nuxt SSG 示例
Nuxt 可以通过静态生成输出页面:
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true,
nitro: {
prerender: {
routes: ['/', '/about', '/articles/1', '/articles/2']
}
}
})
构建命令通常类似:
nuxi generate
生成后的内容可以部署到静态服务器或 CDN。
5. ISR:Incremental Static Regeneration,增量静态再生成
5.1 ISR 是什么
ISR 可以理解为 SSG 的增强版。
SSG 的问题是:页面构建后,如果数据变了,需要重新构建整个站点或相关页面。ISR 允许页面先静态生成,然后在运行时按策略更新部分页面。
简单理解:
第一次:使用构建时生成的静态 HTML
过一段时间:后台重新生成这个页面
后续访问:拿到更新后的静态 HTML
5.2 ISR 的典型策略
常见有两种:
- 按时间重新生成:比如每 60 秒最多更新一次。
- 按事件触发更新:比如 CMS 发布文章后,通过 webhook 通知框架重新生成页面。
5.3 ISR 的优点
- 接近 SSG 的访问速度。
- 数据可以自动更新。
- 不需要每次请求都 SSR,服务端压力较小。
- 适合大量内容页面。
5.4 ISR 的缺点
- 数据不是绝对实时。
- 需要理解缓存和重新生成时机。
- 用户可能短时间内看到旧内容。
- 部署平台需要支持相关能力。
5.5 ISR 适用场景
适合:
- 商品详情页
- 新闻详情页
- 博客文章页
- 课程详情页
- 文档页面
- 大量内容页,但不要求秒级实时
不太适合:
- 金融交易
- 秒杀库存
- 实时权限判断
- 用户个人中心
- 必须每次请求都准确的数据页
5.6 Next.js ISR 示例:按时间更新
// app/products/[id]/page.tsx
interface ProductPageProps {
params: {
id: string
}
}
async function getProduct(id: string) {
const response = await fetch(`https://example.com/api/products/${id}`, {
next: {
revalidate: 60
}
})
if (!response.ok) {
throw new Error('商品加载失败')
}
return response.json()
}
export default async function ProductPage({ params }: ProductPageProps) {
const product = await getProduct(params.id)
return (
<main>
<h1>{product.name}</h1>
<p>价格:{product.price}</p>
<p>库存:{product.stock}</p>
</main>
)
}
关键点:
next: {
revalidate: 60
}
这表示页面相关数据最多缓存 60 秒。超过时间后,下一次访问会触发重新验证和页面更新。
5.7 Next.js ISR 示例:页面级 revalidate
// app/news/[id]/page.tsx
export const revalidate = 300
export default async function NewsPage({ params }: { params: { id: string } }) {
const response = await fetch(`https://example.com/api/news/${params.id}`)
const news = await response.json()
return (
<main>
<h1>{news.title}</h1>
<article>{news.content}</article>
</main>
)
}
revalidate = 300 表示这个页面大约每 300 秒可以重新生成一次。
6. 其他常见说法
除了 CSR、SSR、SSG、ISR,前端渲染体系里还经常出现下面这些词。
6.1 SPA:Single Page Application,单页应用
SPA 指单页应用。它通常和 CSR 一起出现,但二者不是完全等价。
SPA 的特点:
- 初次加载一个 HTML。
- 后续页面切换由前端路由控制。
- 页面不整体刷新。
- 常见于 Vue Router、React Router 应用。
示例:
// router.ts
import { createRouter, createWebHistory } from 'vue-router'
export const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('./pages/Home.vue') },
{ path: '/users', component: () => import('./pages/UserList.vue') }
]
})
后台管理系统大多是 SPA + CSR。
6.2 MPA:Multi-Page Application,多页应用
MPA 指多页应用。传统网站通常是 MPA。
特点:
- 每个页面都有独立 HTML。
- 页面跳转时浏览器会请求新页面。
- 可以由后端模板、SSR、SSG 等方式生成。
传统后端模板示例:
<!-- article.html -->
<html>
<body>
<h1>{{ article.title }}</h1>
<p>{{ article.summary }}</p>
</body>
</html>
6.3 Prerender:预渲染
Prerender 是一个比较泛的词,意思是提前把页面渲染成 HTML。
它可能指:
- 构建时预生成 HTML,接近 SSG。
- 对部分路由生成 HTML,用于改善 SEO。
- 使用爬虫式工具打开页面并保存渲染结果。
Vite SPA 项目如果需要少量 SEO 页面,有时会使用预渲染插件。
6.4 Dynamic Rendering:动态渲染
Dynamic Rendering 通常指:
- 普通用户访问时返回 CSR 页面。
- 搜索引擎爬虫访问时返回预渲染或 SSR 页面。
它更多是历史过渡方案。现在如果 SEO 很重要,通常更推荐直接使用 SSR、SSG 或 ISR。
6.5 Streaming SSR:流式服务端渲染
普通 SSR 往往要等完整 HTML 准备好后再返回。Streaming SSR 可以把 HTML 分块逐步返回。
好处:
- 用户能更早看到页面骨架或部分内容。
- 慢数据区域可以后返回。
- 适合复杂页面。
React Suspense 示例:
import { Suspense } from 'react'
function ProductInfo() {
return <section>商品基础信息</section>
}
async function Reviews() {
const response = await fetch('https://example.com/api/reviews')
const reviews = await response.json()
return <section>{reviews.length} 条评价</section>
}
export default function ProductPage() {
return (
<main>
<ProductInfo />
<Suspense fallback={<p>评价加载中...</p>}>
<Reviews />
</Suspense>
</main>
)
}
6.6 Edge Rendering / Edge SSR,边缘渲染
Edge Rendering 是把渲染逻辑放到离用户更近的边缘节点上执行。
例如用户在北京,就尽量在北京附近的边缘节点生成页面,而不是回源到很远的中心服务器。
适合:
- 全球用户访问
- 低延迟要求
- 个性化但逻辑不太重的页面
注意:边缘运行时通常有限制,不一定支持完整 Node.js API。
6.7 Islands Architecture,岛屿架构
岛屿架构的思想是:页面大部分内容是静态 HTML,只有少数需要交互的区域加载 JS。
比如一篇文章页面:
- 标题、正文、作者信息是静态的。
- 评论框、点赞按钮、收藏按钮是可交互的岛屿。
好处:
- 大幅减少客户端 JS。
- 首屏快。
- SEO 好。
- 适合内容站。
Astro 是岛屿架构的代表框架。
Astro 示例:
---
import LikeButton from '../components/LikeButton.jsx'
const article = await fetchArticle(Astro.params.id)
---
<main>
<h1>{article.title}</h1>
<article>{article.content}</article>
<LikeButton client:load articleId={article.id} />
</main>
client:load 表示这个组件需要在客户端加载并具备交互能力。
6.8 Partial Hydration,部分水合
部分水合指只让页面中需要交互的部分加载 JS 并绑定事件。
它和岛屿架构关系很近,目标都是减少不必要的客户端 JS。
6.9 Progressive Hydration,渐进水合
渐进水合指页面不是一次性完成所有 hydration,而是根据优先级逐步完成。
例如:
- 首屏按钮先水合。
- 页尾评论区等用户滚动到附近再水合。
- 低优先级组件延迟水合。
6.10 RSC:React Server Components,React 服务端组件
RSC 是 React 的一种新组件模型。它允许某些组件只在服务端执行,不把对应 JS 发送到浏览器。
它和 SSR 有关系,但不是同一个概念。
区别:
| 概念 | 重点 |
|---|---|
| SSR | 把页面在服务端渲染成 HTML |
| RSC | 组件可以在服务端执行,并减少客户端 JS |
Next.js App Router 默认组件就是 Server Component。
// 默认是 Server Component
export default async function ArticleList() {
const response = await fetch('https://example.com/api/articles')
const articles = await response.json()
return (
<ul>
{articles.map((article: { id: number; title: string }) => (
<li key={article.id}>{article.title}</li>
))}
</ul>
)
}
如果组件需要浏览器事件,需要加 'use client':
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
7. 不同模式对比
7.1 核心对比表
| 模式 | HTML 生成时机 | HTML 生成位置 | SEO | 首屏 | 数据实时性 | 服务器压力 | 典型场景 |
|---|---|---|---|---|---|---|---|
| CSR | 浏览器运行时 | 客户端 | 较弱 | 依赖 JS | 高 | 低 | 后台、控制台、复杂交互 |
| SSR | 每次请求时 | 服务端 | 好 | 好 | 高 | 高 | 内容详情、电商、实时页面 |
| SSG | 构建时 | 构建机器 | 好 | 很好 | 低 | 很低 | 文档、博客、官网 |
| ISR | 构建时 + 运行时增量更新 | 服务端/CDN 平台 | 好 | 很好 | 中 | 中低 | 商品、新闻、课程、文章 |
7.2 按业务场景选型
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 后台管理系统 | CSR | 登录后访问,SEO 不重要,交互复杂 |
| 企业官网 | SSG | 内容稳定,访问速度和 SEO 重要 |
| 博客 | SSG 或 ISR | 文章不需要每次实时计算 |
| 新闻站首页 | SSR 或 ISR | 需要 SEO,也需要一定更新频率 |
| 新闻详情页 | ISR | 页面多,适合静态缓存和增量更新 |
| 商品详情页 | ISR 或 SSR | 商品信息可缓存,价格库存强实时时用 SSR/API 补充 |
| 搜索结果页 | SSR 或 CSR | 搜索结果实时性强,也可登录后 CSR |
| 用户个人中心 | CSR 或 SSR | 个性化强,SEO 不重要 |
| 文档站 | SSG | 内容稳定,CDN 友好 |
| 股票行情 | CSR + 实时 API | 数据变化快,HTML 静态意义不大 |
| 营销活动页 | SSG 或 SSR | 需要 SEO 和首屏表现 |
8. 实际项目里怎么组合
真实项目通常不是只用一种模式,而是组合使用。
8.1 电商项目示例
| 页面 | 推荐模式 |
|---|---|
| 首页 | ISR |
| 商品列表 | SSR 或 ISR |
| 商品详情 | ISR |
| 购物车 | CSR |
| 订单页 | CSR |
| 支付页 | SSR 或 CSR |
| 帮助中心 | SSG |
商品详情页可以这样拆:
- 商品标题、图片、描述:ISR 生成。
- 实时价格:客户端请求 API 更新。
- 实时库存:客户端请求 API 更新。
- 推荐商品:可 ISR 或 CSR。
示例:
// app/products/[id]/page.tsx
import StockPanel from './StockPanel'
export const revalidate = 300
export default async function ProductPage({ params }: { params: { id: string } }) {
const response = await fetch(`https://example.com/api/products/${params.id}`)
const product = await response.json()
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
<StockPanel productId={params.id} />
</main>
)
}
// app/products/[id]/StockPanel.tsx
'use client'
import { useEffect, useState } from 'react'
export default function StockPanel({ productId }: { productId: string }) {
const [stock, setStock] = useState<number | null>(null)
useEffect(() => {
fetch(`/api/products/${productId}/stock`)
.then((response) => response.json())
.then((data) => setStock(data.stock))
}, [productId])
return <p>实时库存:{stock ?? '加载中...'}</p>
}
这样既保留了 ISR 的 SEO 和速度,又能保证关键动态数据及时更新。
8.2 后台系统示例
后台系统通常更适合 CSR:
<script setup lang="ts">
import { onMounted, ref } from 'vue'
interface User {
id: number
name: string
role: string
}
const users = ref<User[]>([])
onMounted(async () => {
const response = await fetch('/api/admin/users')
users.value = await response.json()
})
</script>
<template>
<table>
<thead>
<tr>
<th>姓名</th>
<th>角色</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.name }}</td>
<td>{{ user.role }}</td>
</tr>
</tbody>
</table>
</template>
原因:
- 页面需要登录。
- SEO 没意义。
- 权限和状态都在客户端交互中频繁变化。
- 表格、表单、弹窗、筛选很多,CSR 开发效率高。
9. 常见性能指标如何受影响
学习渲染模式时,也要理解几个性能指标。
9.1 FCP:First Contentful Paint
首次内容绘制。用户第一次看到文字、图片等内容的时间。
SSG、SSR、ISR 通常对 FCP 更友好,因为 HTML 里已经有内容。
9.2 LCP:Largest Contentful Paint
最大内容绘制。通常衡量首屏最大图片或文本块出现的时间。
SSR/SSG/ISR 可以改善 LCP,但图片优化、接口速度、CDN 同样重要。
9.3 TTI:Time to Interactive
页面变得可交互的时间。
SSR 虽然能快速展示内容,但如果 JS 很大,hydration 仍然可能很慢。
9.4 TTFB:Time to First Byte
浏览器收到第一个字节的时间。
- CSR 静态资源通常 TTFB 低。
- SSR 每次要服务端计算,TTFB 可能更高。
- SSG/ISR 命中缓存时 TTFB 通常较低。
10. 常见坑
10.1 SSR 中直接使用 window 或 document
错误示例:
const width = window.innerWidth
SSR 时服务端没有 window,会报错。
修复方式:
import { onMounted, ref } from 'vue'
const width = ref(0)
onMounted(() => {
width.value = window.innerWidth
})
React 中可以放到 useEffect:
'use client'
import { useEffect, useState } from 'react'
export function WindowWidth() {
const [width, setWidth] = useState(0)
useEffect(() => {
setWidth(window.innerWidth)
}, [])
return <span>{width}</span>
}
10.2 Hydration mismatch
服务端渲染出来的 HTML 和客户端首次渲染结果不一致,就可能出现 hydration mismatch。
常见原因:
- 服务端和客户端时间不同。
- 使用随机数。
- 根据浏览器环境渲染不同内容。
- 服务端数据和客户端数据不一致。
容易出问题的代码:
export default function Page() {
return <p>{Date.now()}</p>
}
服务端生成一个时间,客户端 hydration 时又生成另一个时间,结果不一致。
10.3 把所有页面都 SSR
SSR 不是银弹。后台系统、强交互页面、SEO 不重要页面,不一定需要 SSR。
错误思路:
SSR 首屏快,所以所有页面都 SSR
更合理的思路:
需要 SEO 或首屏内容可见性的页面才优先考虑 SSR/SSG/ISR
强交互和登录后页面优先考虑 CSR
10.4 ISR 误以为是实时
ISR 不是实时渲染。它有缓存周期和重新生成过程。
如果页面必须每次请求都返回最新数据,应该用 SSR 或客户端实时 API。
10.5 SSG 页面太多导致构建很慢
如果有几百万个商品详情页,全部 SSG 会导致构建非常慢。
此时可以考虑:
- 热门页面预生成。
- 长尾页面按需生成。
- 使用 ISR。
- 使用 SSR + 缓存。
11. 简单决策流程
可以按下面的问题判断:
11.1 页面需要 SEO 吗?
- 不需要:优先 CSR。
- 需要:继续判断。
11.2 页面内容是否经常变化?
- 很少变化:SSG。
- 偶尔变化:ISR。
- 每次访问都要最新:SSR。
11.3 页面是否高度个性化?
- 是:CSR 或 SSR。
- 否:SSG 或 ISR。
11.4 页面是否有大量交互?
- 是:CSR 或局部客户端组件。
- 否:SSG/SSR/ISR 都可以。
可以粗略记成:
后台系统:CSR
静态内容:SSG
内容多且会更新:ISR
强实时 + SEO:SSR
静态内容 + 少量交互:岛屿架构 / 部分水合
12. 常见框架能力对照
| 框架 | CSR | SSR | SSG | ISR | 说明 |
|---|---|---|---|---|---|
| Vue + Vite | 强 | 需要额外方案 | 需要插件或框架 | 弱 | 适合 SPA 和后台 |
| Nuxt | 强 | 强 | 强 | 有相关能力 | Vue SSR/SSG 主流方案 |
| React + Vite | 强 | 需要额外方案 | 需要插件或框架 | 弱 | 适合 SPA |
| Next.js | 强 | 强 | 强 | 强 | React 全栈框架 |
| Astro | 支持 | 支持 | 强 | 视部署而定 | 内容站和岛屿架构强 |
| SvelteKit | 强 | 强 | 强 | 视部署而定 | Svelte 全栈框架 |
13. 学习建议
建议按这个顺序学:
- 先学 CSR:理解浏览器、路由、组件、接口请求。
- 再学 SSR:理解服务端生成 HTML、水合、服务端数据获取。
- 再学 SSG:理解构建时生成页面、静态部署、CDN。
- 再学 ISR:理解缓存、重新验证、内容更新策略。
- 最后学 Streaming SSR、RSC、岛屿架构、部分水合等进阶概念。
实践路线:
- 用 Vue 或 React 写一个 CSR 后台列表页。
- 用 Next.js 或 Nuxt 写一个 SSR 文章详情页。
- 用 SSG 生成一个博客。
- 用 ISR 做一个商品详情页。
- 对比每种页面的 HTML 源码、首屏速度和接口请求时机。
学习时可以重点观察:
- 打开“查看网页源代码”,HTML 里有没有真实内容。
- 打开 Network 面板,看数据接口是在服务端请求还是浏览器请求。
- 禁用 JavaScript 后,页面还能不能看到核心内容。
- 刷新页面时,首屏内容出现速度如何。
- 搜索引擎是否能直接读取内容。
14. 总结
CSR、SSR、SSG、ISR 都是前端页面渲染策略,不是单纯的框架名词。
最重要的区别是:
| 模式 | 核心问题答案 |
|---|---|
| CSR | HTML 在浏览器里由 JS 生成 |
| SSR | HTML 在服务端每次请求时生成 |
| SSG | HTML 在构建阶段提前生成 |
| ISR | HTML 先静态生成,再按策略增量更新 |
真正做项目时,不要追求某一种模式“最先进”,而要看业务需要:
- SEO 重要吗?
- 首屏速度重要吗?
- 数据要多实时?
- 页面是不是个性化?
- 服务器成本能不能接受?
- 页面交互复杂吗?
能回答这些问题,就能判断该用 CSR、SSR、SSG、ISR,还是它们的组合。