私以为SSR已经算极大的可以加速页面打开速度,但是其实理解的太片面了,SSR顶多是每次在服务端提前把脱水页面绘制好,通过网络请求交给前端,前端再进行一个展示;流程上虽然比CSR方便很多,但是其实每次还是有服务端的渲染流程,因此对服务端性能消耗比较高;所以由此延伸出了ISR;
先来看SSR和ISR的对比:
- SSR (Server-Side Rendering): 传统的服务器端渲染。每次页面请求都在服务器上完成渲染,然后返回完整的 HTML。
- ISR (Incremental Static Regeneration): 增量静态再生。它是 SSR 和 SSG 的混合体,提前生成静态页面,但允许在后台按需或按间隔增量地重新渲染和更新这些页面。
下面我们通过一个详细的对比表格和讲解来剖析它们的异同与优势。
核心异同对比表
特性 | SSR (Server-Side Rendering) | ISR (Incremental Static Regeneration) |
工作原理 | 用户请求时,服务器实时渲染页面,返回 HTML。 | 预先生成静态页面,并可在后台增量更新特定页面。 |
性能表现 | 首屏加载快,但 TTFB 可能较慢(因服务器需实时计算)。 | 极致性能(直接提供静态文件),TTFB 极低,类似 CDN。 |
可扩展性 | 相对较差。每个动态请求都消耗服务器资源,需要更强大的服务器或集群。 | 极佳。大部分请求由 CDN 响应,服务器压力极小,成本低。 |
数据实时性 | 高。每次请求都使用最新数据渲染,适合数据变化频繁的场景。 | 可控的延迟。可以设定重新验证时间,在“速度”和“新鲜度”间取得平衡。 |
SEO 支持 | 完美支持。爬虫每次都能收到完全渲染的内容。 | 完美支持。爬虫收到的是静态 HTML,内容始终可用。 |
典型使用场景 | 高度动态、用户个性化的页面(如 Dashboard、社交网络动态)。 | 数据更新不极度频繁的大规模网站(如新闻站、电商产品页、博客)。 |
构建时影响 | 无构建时渲染,所有工作在运行时完成。 | 首次构建时生成页面,后续可触发增量再生。 |
技术代表 | Next.js (Node.js), Nuxt.js (Vue), 传统后端框架 (PHP, Ruby) | Next.js (V9.5+ 独家特性),是 SSG 的强大演进。 |
深入解析与优势分析
SSR 的优势与特点
- 始终如一的实时数据: 这是 SSR 最大的优势。对于每个用户请求,页面都会用最新的数据重新渲染。这对于股票行情、实时体育比分、个人用户数据等场景是必须的。
- 简单的数据获取: 在 Next.js 等框架中,使用
getServerSideProps函数,可以很自然地在服务器端获取页面所需的所有数据。
- 首屏性能与 SEO: 解决了 CSR 应用首屏加载白屏和 SEO 不友好的问题。
SSR 的劣势:
- 服务器开销大: 每个请求都需要服务器进行渲染,CPU 和内存消耗高,尤其是在流量高峰时期。
- TTFB 可能较慢: 因为要等待数据获取和渲染,首字节时间可能比直接返回静态文件要长。
- 缓存更复杂: 虽然可以对页面进行缓存,但对于个性化内容,缓存策略会变得复杂。
ISR 的优势与特点 (Next.js 范例)
ISR 可以看作是 “带缓存的 SSR” 或 “可更新的 SSG”。它的核心优势在于平衡了性能、规模和内容新鲜度。
- 极致的性能与扩展性:
- 页面作为静态文件提供,拥有和 SSG 一样的极快加载速度和极低的 TTFB。
- 通过全球 CDN 分发,可以轻松应对海量流量,服务器成本极低。
- 增量更新 - 核心创新点:
- 背景再生: 当访问一个过期的页面(超过了设定的
revalidate时间)时,ISR 会立即返回旧的、已缓存的页面,同时在后台触发一次新的渲染。下次访问时,将得到新的页面。 - 按需再生: Next.js 还支持
getStaticProps配合revalidate,以及更高级的 On-Demand Revalidation,允许你通过 API 路由手动触发特定页面的再生(例如,当 CMS 内容更新时)。
- 永不丢失的页面:
- 即使后台再生失败,网站依然可以继续服务于旧的、可用的静态页面,保证了网站的可用性。
ISR 的劣势:
- 数据非绝对实时: 存在一个“时间窗口”,用户可能看到的是稍旧的数据(直到下一次再生完成)。不适合要求绝对实时的场景。
- 复杂性: 缓存失效和按需再生的逻辑需要精心设计。
- 平台锁定: 目前 ISR 主要是 Next.js 的招牌特性,虽然概念可以被其他平台借鉴,但原生支持最好的是 Vercel 平台。
那么在NextJS里如何做ISR:
首先你得辨别一下,什么页面适合做ISR,一般首屏展示速度要求很高的页面,并且变化频率比较低的页面适合,比如说网站的Landing Page;
给一个例子:
src/app/page.tsx
import { TryIt } from '@/components/TryIt' import { NavBar } from '@/components/NavBar' import { CopyRightFooter } from '@/components/CopyRightFooter' import { HeroSection } from '@/components/HeroSection' import { WorksGuide } from '@/components/WorksGuide' import { FeatureSection } from '@/components/FeatureSection' import { TheresAnAiForThat } from '@/components/TheresAnAiForThat' import { EndorsementSection } from '@/components/EndorsementSection' import { TestimonialsSection } from '@/components/TestimonialsSection' // 静态生成配置 export const revalidate = 3600 // 1小时重新验证一次 export const dynamic = 'force-static' // 强制静态生成 export default function Home() { return ( <div className="min-h-screen bg-gradient-to-b from-background to-muted/30"> <NavBar /> {/* Hero Section */} <HeroSection /> {/* Theres An AI For That */} <TheresAnAiForThat /> {/* Works Guide */} <WorksGuide /> {/* Endorsement Section */} <EndorsementSection /> {/* Testimonials Section */} <TestimonialsSection /> {/* Feature Section */} <FeatureSection /> {/* CTA Footer */} <TryIt /> {/* Footer */} <CopyRightFooter /> </div> ) }
可以看到,这个页面不是客户端组件,同时还配置了静态生成配置1个小时,那么意味着一个小时内的用户访问都是直接把这个页面交给用户,服务端只需要渲染好一次,后续访问都不需要再次渲染,相比起SSR,每次服务端渲染好,交给客户端相比就又节省了时间。
同时还应该加一个配置到next.config.ts:
const nextConfig: NextConfig = withAxiom({ output: 'standalone', assetPrefix: '/synth-speak', reactStrictMode: false, // 添加缓存头配置(仅浏览器缓存,无CDN) async headers() { return [ { source: '/', headers: [ { key: 'Cache-Control', value: 'public, max-age=3600, stale-while-revalidate=86400', }, // 添加ETag支持,部署时自动失效 { key: 'ETag', value: `"${process.env.BUILD_ID || Date.now()}"`, }, ], }, ] },