React Server Components
RSC 的概念、Server 与 Client 组件的分工、Suspense 与流式渲染机制。
#tech / dev / frame
#resource / react
#type / concept
#status / growing
[!info] related notes
- 所属 MOC: React MOC
- 上位主题: react
- 相关概念: react-hooks, react-virtual-dom
React Server Components
定义
React Server Components (RSC) 是一种组件模型,允许部分组件只在服务端执行,其渲染结果(序列化的组件树)被流式发送到客户端。客户端组件仍然在浏览器中运行并拥有交互能力。
核心区别:
- Server Component:在服务端运行,可以访问文件系统、数据库等,不发送 JS 到客户端
- Client Component:在客户端运行,可以使用 Hooks、浏览器 API,有交互能力
为什么需要 RSC
传统 SSR 的问题:
- 服务端渲染 HTML 后,客户端需要下载所有 JS 并”hydrate”整个应用
- 即使某些组件不需要交互(如文章内容),其 JS 仍然被发送和执行
- 数据获取通常要在客户端重新执行
RSC 的优势:
- Server Component 的 JS 不发送到客户端,减少 bundle 大小
- 可以直接在组件内 async/await 获取数据,不需要 useEffect
- 数据获取和渲染在同一层(服务端),避免客户端-服务端往返
Server vs Client 组件
Server Component(默认)
// app/article/page.tsx (Next.js App Router)
// 没有 "use client" 指令 = Server Component
import { db } from '@/lib/db'
export default async function ArticlePage({ params }: { params: { id: string } }) {
// 可以直接访问数据库
const article = await db.article.findUnique({
where: { id: params.id }
})
return (
<article>
<h1>{article.title}</h1>
<p>{article.content}</p>
{/* 引入 Client Component 处理交互 */}
<LikeButton articleId={article.id} />
</article>
)
}
特点:
- 默认就是 Server Component
- 可以是 async 函数
- 可以直接访问服务端资源
- 不能使用 useState、useEffect 等 Hooks
- 不能使用浏览器 API
- 不能使用事件处理函数(onClick 等)
Client Component
"use client" // 必须显式声明
import { useState } from 'react'
export function LikeButton({ articleId }: { articleId: string }) {
const [liked, setLiked] = useState(false)
return (
<button onClick={() => {
setLiked(!liked)
fetch(`/api/articles/${articleId}/like`, { method: 'POST' })
}}>
{liked ? '已点赞' : '点赞'}
</button>
)
}
特点:
- 必须在文件顶部加
"use client"指令 - 可以使用所有 Hooks
- 可以使用浏览器 API
- 可以使用事件处理函数
- 不能直接访问服务端资源
组合规则
- Server Component 可以导入 Client Component
- Client Component 不能导入 Server Component(但可以通过 children 接收)
// Server Component 可以把 Server Component 作为 children 传给 Client Component
// app/layout.tsx
import { ClientLayout } from './ClientLayout'
import { ServerSidebar } from './ServerSidebar'
export default function Layout({ children }) {
return (
<ClientLayout sidebar={<ServerSidebar />}>
{children}
</ClientLayout>
)
}
Suspense 与流式渲染
Suspense
Suspense 允许组件”等待”某些操作完成后再渲染,期间显示 fallback。
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<h1>我的页面</h1>
<Suspense fallback={<div>加载评论中...</div>}>
<Comments /> {/* 这个组件可能需要异步加载 */}
</Suspense>
</div>
)
}
流式渲染
RSC 支持流式发送:页面的静态部分先到达客户端并显示,动态部分(被 Suspense 包裹的部分)完成后逐步流入。
客户端接收到的内容流:
1. 立即到达:<h1>我的页面</h1> + fallback "加载评论中..."
2. 评论加载完成后:<Comments /> 替换 fallback
这比传统 SSR(等所有数据准备好再一次性发送 HTML)的体验更好。
RSC 与传统 SSR 的区别
| 传统 SSR | RSC | |
|---|---|---|
| 渲染位置 | 服务端渲染 HTML,客户端 hydrate | Server Component 在服务端,Client Component 在客户端 |
| JS 发送 | 所有组件的 JS 都发送到客户端 | 只有 Client Component 的 JS 发送到客户端 |
| 数据获取 | 通常需要在客户端重新获取 | Server Component 可以直接获取,结果序列化发送 |
| 交互 | hydrate 后才能交互 | Client Component 独立交互,不阻塞 |
边界与常见误区
"use client"不是说组件只在客户端运行,而是说它需要在客户端执行(需要 JS)- Server Component 不能使用任何 Hooks 或浏览器 API
- 不要把 Server Component 看成”不需要的优化”,它是默认行为
- RSC 不是取代 SSR,而是和 SSR 互补(SSR 用于首屏 HTML,RSC 用于组件级服务端渲染)
- 在 Next.js App Router 中,所有组件默认是 Server Component
一句话记忆法
Server Component 在服务端运行、不发 JS 到客户端、可以直接拿数据;Client Component 在浏览器运行、有交互能力、需要 "use client" 声明。Suspense 让页面可以流式加载。