React Server Components(RSC)是React架构层面的重大演进,将组件渲染分为服务端和客户端两个执行环境。服务端组件在服务器上渲染为序列化数据流发送到客户端,不携带JavaScript运行时代码,直接访问数据库和文件系统。理解RSC的渲染机制和数据获取模式,是React全栈应用开发的核心技能。
React Server Components运行机制详解
RSC将组件树分为Server Component和Client Component两种类型。Server Component以.server.js后缀或在Server端导入的组件自动识别,其代码不打包进客户端bundle。Client Component以"use client"指令声明,代码发送到浏览器执行。
服务端组件与客户端组件的核心差异:
// app/products/page.tsx (Server Component - 默认)
import { db } from '@/lib/database'
import ProductCard from './ProductCard'
import Filter from './Filter'
// 服务端组件可直接async,直接访问数据库
export default async function ProductsPage() {
const products = await db.query('SELECT * FROM products LIMIT 20')
return (
<div>
{/* Client Component嵌入在Server Component中 */}
<Filter />
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
)
}
// app/products/Filter.tsx
'use client'
import { useState } from 'react'
export default function Filter() {
const [category, setCategory] = useState('all')
return (
<select value={category} onChange={e => setCategory(e.target.value)}>
<option value="all">全部</option>
<option value="electronics">电子产品</option>
</select>
)
}
// app/products/ProductCard.tsx
// 不带use client,作为Server Component
export default function ProductCard({ product }) {
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>¥{product.price}</p>
</div>
)
}
RSC数据获取:fetch缓存策略与Streaming SSR
Next.js App Router基于RSC实现了fetch级别的缓存层。在Server Component中使用fetch时,Next.js自动处理缓存和去重:
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'
// fetch在Server Component中自动缓存
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await fetch(
'https://api.example.com/posts/' + params.slug,
{
next: {
revalidate: 3600, // ISR: 每小时重新验证
tags: ['blog', 'post-' + params.slug], // 按需重验证标签
}
}
).then(r => r.json())
if (!post) notFound()
// 并行数据获取
const [author, comments] = await Promise.all([
fetch('https://api.example.com/users/' + post.authorId, {
next: { revalidate: 1800, tags: ['author-' + post.authorId] }
}).then(r => r.json()),
fetch('https://api.example.com/posts/' + params.slug + '/comments', {
cache: 'no-store' // 不缓存,每次请求都获取最新
}).then(r => r.json())
])
return (
<article>
<h1>{post.title}</h1>
<p>作者: {author.name}</p>
<div dangerouslySetInnerHTML={{__html: post.content}} />
<CommentList comments={comments} />
</article>
)
}
缓存策略选项:revalidate: N设置ISR间隔;cache: 'no-store'禁用缓存;tags用于按需失效。
Streaming SSR与Suspense边界配置
RSC支持流式渲染,页面内容逐步推送到客户端,优先渲染的内容先展示,慢速数据加载的部分用Suspense占位:
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { Stats, RecentOrders, UserList } from './components'
export default function Dashboard() {
return (
<div className="dashboard">
{/* 快速渲染部分,无Suspense包裹 */}
<header>仪表盘</header>
{/* 慢速数据用Suspense包裹,流式推送 */}
<Suspense fallback={<div className="skeleton">加载统计数据...</div>}>
<Stats />
</Suspense>
<Suspense fallback={<div className="skeleton">加载订单...</div>}>
<RecentOrders />
</Suspense>
{/* 嵌套Suspense,外层先resolve的先展示 */}
<Suspense fallback={<div className="skeleton">加载用户...</div>}>
<UserList />
</Suspense>
</div>
)
}
// Stats组件在Server端异步获取数据
async function Stats() {
const stats = await fetch('https://api.example.com/stats', {
next: { revalidate: 60 }
}).then(r => r.json())
return (
<div className="stats-grid">
<div>总订单: {stats.totalOrders}</div>
<div>总收入: ¥{stats.revenue}</div>
</div>
)
}
流式渲染的工作流程:Next.js将RSC树序列化为RSC Payload,Suspense边界将页面切分为多个chunk。已就绪的chunk通过HTTP流式推送,浏览器收到后立即渲染,未就绪的chunk等待数据到达后注入。用户看到的是渐进式页面加载,而非空白等待。
Server与Client组件通信模式
Server Component不能直接向Client Component传递函数,只能传递可序列化数据。需要跨边界传递行为时,使用props传递配置,由Client Component实现交互逻辑:
// Server Component传递props到Client Component
// app/todos/page.tsx (Server)
import TodoApp from './TodoApp'
import { getTodos } from '@/lib/api'
export default async function Page() {
const initialTodos = await getTodos()
return <TodoApp initialTodos={initialTodos} />
}
// app/todos/TodoApp.tsx (Client)
'use client'
import { useState } from 'react'
export default function TodoApp({ initialTodos }: { initialTodos: Todo[] }) {
const [todos, setTodos] = useState(initialTodos)
const toggleTodo = async (id: number) => {
const updated = await fetch('/api/todos/' + id, {
method: 'PATCH',
body: JSON.stringify({ done: true })
}).then(r => r.json())
setTodos(prev => prev.map(t => t.id === id ? updated : t))
}
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.title} {todo.done ? '✓' : ''}
</li>
))}
</ul>
)
}
RSC架构下,初始数据在Server端获取并序列化为props传入Client Component,客户端水合后接管交互。API路由处理后续的状态更新,保持Server Component作为数据入口、Client Component作为交互入口的清晰边界。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/reactservercomponents-shi-zhan-fu-wu-duan-zu-jian-xuan-ran/