Next.js渲染(CSR/SSR/SSG/ISR)
本文概括
本文介绍了 Next.js 的主要渲染方式,包括 CSR、SSR、SSG、ISR,以及 RSC 和 Suspense。
- CSR:客户端渲染,适合交互密集的页面。
- SSR:服务端渲染,首屏快,SEO 友好,适合动态内容。
- SSG:构建时生成静态页面,性能高,适合不常更新的内容。
- ISR:在 SSG 基础上定期刷新页面,兼顾性能与动态性。
- RSC:服务端组件,减小客户端包,支持直接访问后端数据。
- Suspense:延迟渲染和流式渲染,可显示 fallback,优化首屏体验。
渲染方式
CSR(Client-Side Rendering 客户端渲染)
CSR(Client-Side Rendering) :页面在浏览器端渲染,服务器只返回一个空 HTML 和 JS 脚本,浏览器下载并执行 JS 后生成内容,适合交互密集的单页应用,但首屏渲染慢且 SEO 不友好。
在 App Router 中使用:
- 在组件文件顶部加上
'use client'声明,使组件成为 Client Component。 - 页面初始只返回框架 HTML + JS,数据通过
useEffect或fetch在客户端请求并渲染。
特点:
- 首屏加载慢,需要等待 JS 执行。
- SEO 不佳,初始 HTML 内容几乎为空。
- 适合高度交互或用户个性化的页面(如 Dashboard、后台管理系统)。
- 可以使用 React Hooks(如
useState、useEffect)和浏览器 API。
SSR(Server-Side Rendering 服务端渲染)
SSR(Server-Side Rendering) :每次请求时,服务器运行 React 组件生成完整 HTML 并返回浏览器,首屏渲染快且对 SEO 友好,适合内容需要实时更新的页面,但服务器压力较大。
在 App Router 中使用:
- 默认组件为 Server Component,在服务器端生成 HTML。
- 可以在服务端获取数据并直接渲染页面。
特点:
- 每次请求都在服务器生成完整 HTML,无需等待 JS 执行即可显示,SEO 友好。
- 适合动态内容页面,如库存、价格或用户相关信息。
- 性能成本较高,每个请求都要渲染 HTML。
- 限制:Server Component 不能直接使用 CSR 的 Hook(如
useState、useEffect),但可以包含 Client Component。
SSG(Static Site Generation 静态站点生成)
SSG(Static Site Generation) :在构建阶段生成静态 HTML,所有请求直接返回静态页面,性能极高且 SEO 友好,适合内容不经常变动的页面,如博客或文档,但更新需要重新构建。
在 App Router 中使用:
- 默认
fetch为静态缓存(cache: 'force-cache'),即 构建时生成 HTML 并缓存。 - 动态路由可配合
generateStaticParams生成静态路径。
特点:
- 页面直接作为静态文件部署,可通过 CDN 高速分发,首屏渲染快。
- 服务器压力低,性能最佳。
- 内容更新需要重新构建。
- 适合博客、文档、营销页等静态内容页面。
ISR(Incremental Static Regeneration 增量静态再生)
ISR(Incremental Static Regeneration) :在 SSG 基础上允许增量更新,页面首次请求返回静态内容,后台按设定时间重新生成 HTML 更新缓存,兼顾性能和动态性,适合需要定期更新的静态内容。
在 App Router 中使用:
- 在
fetch或getStaticProps中设置revalidate时间,例如revalidate: 10表示页面每 10 秒自动更新一次。 - 可结合动态路由和
generateStaticParams使用,实现增量生成静态页面。
特点:
- 页面性能接近 SSG,首屏渲染快,可通过 CDN 分发。
- 支持定期更新,无需重新构建整个站点。
- 适合内容定期更新但不需要每次请求都动态生成的页面,如新闻列表、电商商品页。
RSC( React Server Component)
RSC 是 React 推出的服务端组件机制,允许在服务端渲染组件,并将渲染后的数据传输到客户端,而不是把整个组件打包到客户端。
- 数据请求在服务端完成,客户端不需要知道数据获取过程。
- 服务端组件生成的 HTML 并不直接可交互,需要客户端 JS 下载并水合(Hydration)后才能交互。
RSC与SSR的区别如下:
| 特性 | SSR | RSC |
|---|---|---|
| 渲染粒度 | 页面级 | 组件级 |
| JS bundle | 服务端渲染的组件会被打包到客户端 | 服务端组件代码不会打包到客户端 |
| 客户端状态 | 页面刷新会丢失 | 客户端状态保持,RSC可以多次获取数据而不刷新页面 |
| 数据获取 | 必须在组件渲染前完成 | 直接在服务端组件中获取数据,组件代码不会暴露到客户端 |
| 水合 (Hydration) | 整个页面 | 选择性水合,客户端只水合需要交互的部分 |
特点:
- 减小客户端 bundle 大小:服务端组件不打包到客户端,只传输渲染后的数据(RSC Payload)。
- 直接访问后端资源:服务端组件中可以直接访问数据库、文件系统或 API,无需通过客户端请求。
- 不能使用客户端 Hook:服务端组件不支持
useState、useEffect等,只能用服务端逻辑。 - 可组合客户端组件:服务端组件可以导入客户端组件实现交互;客户端组件不能导入服务端组件。
渲染流程:
- 服务端渲染 RSC 组件,生成 RSC Payload(包含渲染后的 HTML、数据和样式)。
- 发送到客户端,客户端根据 RSC Payload 重建 React 树。
- 客户端部分交互通过客户端组件实现,不影响服务端组件的数据。
缺点对比:
| 缺点 | SSR | RSC |
|---|---|---|
| 数据获取 | 必须在组件渲染前完成 | 可以分块获取,按需加载 |
| 客户端包大小 | 组件全部打包到客户端 | 服务端组件不打包,减小 bundle |
| 水合时间 | 必须等待整页水合 | 可选择性水合,客户端只水合交互组件 |
| 页面刷新 | 每次刷新页面都会重新渲染 | 客户端状态可以保留,RSC 可重复请求数据而不刷新 |
使用方式:
- 默认组件是 服务端组件,顶部不需要加
'use server'。 - 需要交互的组件加
'use client',导入的模块及子组件都会打包到客户端。 - 服务器组件中可以直接导入客户端组件;客户端组件中不能导入服务器组件。
Suspense
Suspense 是 React 的延迟渲染机制,允许组件在等待数据或资源时显示 fallback(loading 状态),并在数据准备好后再渲染真实内容。在 Next.js 中,Suspense 可以与 RSC 流式渲染(Streaming SSR) 配合使用,实现部分水合和流式页面渲染。特点如下:
- 延迟渲染:数据未准备好时,显示 fallback,页面不会阻塞其他部分渲染。
- 流式渲染 (Streaming SSR) :页面分块渲染,每个组件准备好后立即发送到客户端,提升首屏渲染速度。
- 选择性水合:只对需要交互的组件进行水合,减少客户端 JS 执行量。
- SEO 友好:最终 HTML 会包含全部内容,搜索引擎可抓取。
流式渲染 (Streaming)
- Next.js 将页面拆分成多个块 (chunks),每块准备好就发送给客户端。
- 客户端收到块后立即渲染,不必等整个页面渲染完再显示。
- 传统 SSR 是串行渲染,Streaming SSR 是并行渲染,提高首屏可见速度。
- 页面级别使用loading.tsx,组件级别使用Suspense