React Server Components架构设计理念
React Server Components(RSC)将组件渲染拆分为服务端与客户端两个执行环境。Server Components在服务端执行,可直接访问数据库、文件系统等后端资源,渲染结果以序列化格式(RSC Payload)传输至客户端;Client Components在浏览器执行,保留交互能力与状态管理。两者在组件树中可自由嵌套,React运行时自动协调两端的渲染与hydration。
RSC解决的核心问题:传统SSR将所有数据获取集中在顶层getServerSideProps,瀑布式请求导致TTFB过长;客户端渲染的组件Bundle包含数据获取逻辑与API路由,增加JS体积。RSC让组件自身持有数据获取能力,服务端按需访问数据源,客户端只接收渲染后的HTML与交互代码。
Server Components与Client Components划分原则
默认情况下app目录下组件为Server Components,添加use client指令后变为Client Components。划分原则:
Server Components适用场景:纯数据展示、直接访问数据库或ORM、调用服务端SDK(如读取环境变量、访问文件系统)、无交互逻辑的布局组件。
Client Components适用场景:包含useState/useEffect等Hooks、绑定事件处理器、使用浏览器API(window/location/storage)、依赖第三方客户端库(图表库、富文本编辑器)。
// Server Component
import { db } from '@/lib/db'
export default async function PostsPage() {
const posts = await db.query('SELECT * FROM posts')
return <PostList posts={posts} />
}
// Client Component
'use client'
import { useState } from 'react'
export function PostList({ posts }) {
const [filter, setFilter] = useState('')
const filtered = posts.filter(p => p.title.includes(filter))
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{filtered.map(post => <PostCard key={post.id} post={post} />)}
</div>
)
}
流式SSR与Suspense边界配置
RSC配合Streaming SSR实现渐进式页面渲染:服务端将页面HTML分块发送,已就绪的内容先渲染,数据未就绪的组件通过Suspense fallback显示加载状态,数据到达后React注入替换内容。整个过程中浏览器无需等待全部数据返回即可开始渲染,FCP和LCP指标显著改善。
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<div>
<h1>数据面板</h1>
<Suspense fallback={<Skeleton />}>
<RealtimeChart />
</Suspense>
<Suspense fallback={<Skeleton />}>
<UserStats />
</Suspense>
</div>
)
}
async function RealtimeChart() {
const data = await fetch('https://api.example.com/chart', { cache: 'no-store' })
const chartData = await data.json()
return <Chart data={chartData} />
}
Suspense边界的粒度需平衡用户体验与请求数:边界过粗则慢组件阻塞快组件渲染,边界过细则加载态过多影响视觉连续性。推荐按页面逻辑模块划分:侧边栏、主内容区、图表区各设一个Suspense边界。
数据获取模式对比:Server Actions与Route Handlers
Next.js App Router中数据获取有三种路径:
1. Server Components直接async/await:适合页面首次加载,组件渲染时同步获取数据,请求在服务端执行,结果直接嵌入RSC Payload。
2. Server Actions(use server):适合表单提交和数据变更,服务端执行函数客户端触发调用。自动处理乐观更新与缓存失效:
'use server'
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.query('INSERT INTO posts (title, content) VALUES (?, ?)', [title, content])
revalidatePath('/posts')
}
3. Route Handlers(app/api/):传统REST API风格,适合第三方回调或非React客户端调用,返回标准Response对象。
优先选择Server Components直取 + Server Actions变更,减少中间API层。Route Handlers仅在Webhook、第三方集成等必须暴露HTTP端点时使用。
缓存策略与Revalidation配置
RSC默认缓存fetch请求(与Pages Router不同)。控制缓存行为:
// 默认缓存(静态页面)
const data = await fetch('https://api.example.com/data')
// 禁用缓存(实时数据)
const data = await fetch('https://api.example.com/realtime', { cache: 'no-store' })
// 定时重新验证(ISR)
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }
})
revalidatePath和revalidateTag可在Server Actions或Route Handlers中触发定向缓存失效。revalidatePath刷新/posts路径下所有缓存数据,revalidateTag刷新所有标记为posts标签的fetch请求缓存。定向失效比全站revalidate高效得多。
RSC Payload序列化与传输优化
RSC Payload是React自定义的二进制序列化格式,包含渲染后的组件树、客户端组件引用和序列化数据。相比传统SSR的完整HTML,RSC Payload体积更小且支持增量更新:导航到新页面时仅传输差异部分的Payload,避免全页面刷新。
Server Components传递给Client Components的props必须可序列化:支持string/number/boolean/array/object/Date/URL/Promise,不支持function/class/Error/Map/Set。函数需通过Server Actions传递,Map/Set转换为基础类型。
大型数据传输场景下,Server Components可将数据引用传递给Client Components,由Client Component按需加载,避免首屏Payload过大:
async function PostPreview({ postId }) {
const summary = await getPostSummary(postId)
return <PostCard summary={summary} postId={postId} />
}
'use client'
function PostCard({ summary, postId }) {
const loadFull = async () => {
const res = await fetch(`/api/posts/${postId}`)
}
return <div onClick={loadFull}>{summary}</div>
}
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/reactservercomponents-fu-wu-duan-zu-jian-yuan-li-yu-liu-shi/