2026年,React Server Components(简称RSC)不再是概念实验。State of React 2026的数据显示,超过50%的开发者想在生产环境用上它。但它到底是什么?跟传统SSR区别在哪?在Next.js之外怎么看?
这篇文章主要讲它是什么 → 怎么写代码 → 跟其他方案怎么比。

第一部分:React Server Components是什么,解决了什么问题
问题的起点
传统的React应用是纯客户端渲染(CSR)。浏览器下载整个JS包,在客户端构建DOM树、获取数据、渲染界面。这个过程有几个痛点:
– 用户首屏要等JS下载→编译→执行→数据请求→渲染,链条很长
– 低端设备处理大JS包很吃力
– 大部分组件并没有交互需求(文章正文、列表项),但JS仍然要发到浏览器
为了解决这些问题,React引入了服务端渲染(SSR)。SSR的思路是在服务端把组件渲染成HTML再发给浏览器,让用户更快看到内容。但这只是「披了一层HTML的CSR」——浏览器仍然要下载完整的JS包并进行水合(Hydration),把静态HTML变成可交互的React应用。
React Server Components的切入点完全不同。RSC不是让所有组件都在服务端渲染,而是让部分组件只在服务端运行,完全不发送JS到浏览器。
RSC vs SSR vs CSR:本质区别
理解RSC的关键在于它和SSR是两个正交的概念:
| / | CSR | SSR | RSC |
|---|---|---|---|
| 渲染位置 | 浏览器 | 服务端→浏览器 | 服务端(部分组件) |
| JS是否发给浏览器 | 全部 | 全部(仍需水合) | 只发Client Component |
| 数据获取 | 客户端请求API | 服务端可预取 | 直接访问数据库/FS |
| 交互支持 | 完整 | 完整(水合后) | 不支持 |
| 包体积影响 | 完整包 | 完整包 | 仅交互部分 |
RSC 的核心工作方式
RSC的渲染流程可以拆成几步:
1. 服务端收到请求,从根组件开始渲染。根组件必须是Server Component
2. 遇到 Server Component,正常执行,生成React元素树
3. 遇到 `’use client’` 标记的 Client Component,不执行内部逻辑,而是生成一个占位符加模块引用,交给浏览器后续处理
4. 服务端把渲染结果序列化成一种特殊格式(叫做Flight payload),流式发送到浏览器
5. 浏览器收到后,重建React树——Server Component部分直接用序列化后的结果,Client Component部分按模块引用下载JS并渲染
关键区别在于:Server Component的输出不是HTML,而是一个序列化的React元素树。这让React可以在客户端精确恢复组件结构,同时零成本地跳过不需要交互的部分。
第二部分:上代码
Server Component 基础
默认情况下,Next.js App Router中的所有组件都是Server Component。不需要额外声明:
```tsx
// app/posts/page.tsx — 这是一个 Server Component
import { db } from '@/lib/database'
export default async function PostsPage() {
const posts = await db.post.findMany({ take: 20 })
return (
<div>
<h1>最新文章</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
)
}
```
这个组件:
– 在服务端执行,数据库查询zero round-trip
– 返回给浏览器的只有最终HTML
– 没有JS bundle——因为它没有任何交互
Client Component 标记
需要交互的时候,加 `’use client’`:
```tsx
// app/components/LikeButton.tsx
'use client'
import { useState } from 'react'
export default function LikeButton({ postId }: { postId: number }) {
const [liked, setLiked] = useState(false)
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️ 已赞' : '🤍 点赞'}
</button>
)
}
```
混用的正确姿势
Server Component不能直接import Client Component用来渲染——但可以通过children(组合模式)来嵌套。这是RSC架构中最核心的设计模式:
```tsx
// app/posts/page.tsx (Server Component)
import { db } from '@/lib/database'
import LikeButton from './LikeButton' // Client Component
export default async function PostsPage() {
const posts = await db.post.findMany({ take: 20 })
return (
<div>
{posts.map(post => (
<PostCard key={post.id} post={post}>
<LikeButton postId={post.id} />
</PostCard>
))}
</div>
)
}
```
```tsx
// ❌ 错误:不能在 Client Component 中 import Server Component
'use client'
import ServerComponent from './ServerComponent' // 报错!
export default function ClientComponent() {
return <ServerComponent />
}
```
```tsx
// ✅ 正确:通过 children 传递
'use client'
export default function ClientComponent({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>
}
```
数据获取在 Server Component 中
React Server Components最大的优势之一就是数据获取可以直接写进组件,不需要额外封装一套API:
```tsx
// Server Component — 直接查数据库,零客户端JS
export default async function UserDashboard({ userId }: { userId: string }) {
const user = await db.user.findUnique({ where: { id: userId } })
const posts = await db.post.findMany({ where: { authorId: userId } })
return (
<div>
<h1>{user.name} 的仪表盘</h1>
<p>共 {posts.length} 篇文章</p>
</div>
)
}
```
对比客户端方案——需要多一层API路由和状态管理:
```tsx
'use client'
import { useEffect, useState } from 'react'
export default function UserDashboard({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
const [posts, setPosts] = useState([])
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(data => {
setUser(data.user)
setPosts(data.posts)
})
}, [userId])
if (!user) return <div>加载中...</div>
return (
<div>
<h1>{user.name} 的仪表盘</h1>
<p>共 {posts.length} 篇文章</p>
</div>
)
}
```
Server Actions:表单处理
React 19引入的Server Actions让表单提交可以直接调服务端函数:
```tsx
// app/actions.ts
'use server'
import { db } from '@/lib/database'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
await db.post.create({ data: { title, content } })
revalidatePath('/posts')
}
```
```tsx
// app/posts/new/page.tsx — Server Component
import { createPost } from './actions'
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" placeholder="文章标题" required />
<textarea name="content" placeholder="内容" />
<button type="submit">发布</button>
</form>
)
}
```
不需要写API路由、不需要fetch。
第三部分:竞品对比
RSC不是唯一的「减少客户端JS」方案。
RSC vs Astro
核心思路差异:
– RSC:在服务端渲染部分组件,保留完整的React交互能力
– Astro:默认零JS,按需加载部分交互组件(Islands架构),可以用React/Vue/Svelte等框架
选型建议:
| 场景 | RSC | Astro |
|---|---|---|
| 内容型网站(博客/文档) | 可用 | 更优 |
| 高交互应用(仪表盘/SaaS) | 更优 | 需要额外工作 |
| 多框架团队 | 仅React | 任意框架 |
| 需要完整React生态 | 原生支持 | 有限支持 |
Astro在内容型站点上更激进——默认不发送任何JS。一个纯展示的博客页面,Astro的输出只有HTML+CSS,零JS。而RSC在这个场景虽然也能做到零JS,但在Next.js框架层还是有一些runtime开销。
RSC vs Qwik
Qwik的核心思路是「可恢复性」——不发送交互代码,直到用户触发交互时才下载对应的JS片段。
区别在于:
– RSC:数据获取和渲染在服务端执行,Client Component发送完整JS
– Qwik:所有组件的JS都被懒加载到极致,用户点击按钮时才会下载handler
Qwik在首屏包体积上更激进,但它需要学习一套全新的框架($前缀、序列化、符号系统),生态远不如React成熟。如果你的团队已经熟悉React,Qwik的迁移成本很高。
RSC vs 传统SSR
很多React开发者已经在用SSR了,RSC比SSR多做了什么?
– SSR仍然需要把全套JS发给浏览器做水合
– RSC把非交互组件的JS完全剔除
– SSR的数据获取限制在 getServerSideProps 等固定API中,RSC可以在任意组件层级直接获取
developerway.com有一组实测数据:在产品页面这样的简单内容场景下,RSC让LCP从SSR的2.1s降到了1.1s。但在交互密集的仪表盘场景,RSC反而比CSR更慢——服务端渲染有时间开销。
选型参考
“`
你的应用交互密集吗?
├── 是 → RSC 需要搭配大量 Client Component
│ 可以考虑:传统 CSR + 代码分割
│
└── 否 → 主要是内容展示?
├── 是 → 项目使用 React 吗?
│ ├── 是 → RSC 是不错的选择
│ └── 否 → 考虑 Astro
└── 否 → 混合型
RSC 适合,但需要做好边界规划
“`
总结
React Server Components是React自Hooks以来最大的一次架构革新。它不是「优化技巧」,而是一种新的思考方式——让服务端做服务端擅长的,让浏览器做浏览器擅长的。
在2026年,如果你在Next.js生态中开发内容型或有明确边界的应用,React Server Components值得认真考虑。但如果你需要在重度交互应用中追求零客户端JS,Astro或Qwik可能是更合适的起点。
关键不在于「它好不好」,而在于你的应用是什么、你的团队是谁。
📖 推荐阅读
看看这些跟react相关的文章,你可能会感兴趣:
用 Electron 构建桌面应用:React/Vue 前端框架集成实战指南
浅析:Next.js 15 为什么成为 Agentic AI 应用标准?