React Server Components核心概念
React Server Components(RSC)是React架构层面引入的渲染模式,将组件划分为服务端组件和客户端组件。服务端组件在服务器上执行,可以直接访问数据库和文件系统,渲染结果以序列化格式发送到浏览器;客户端组件使用”use client”指令标记,在浏览器中执行,处理交互逻辑。这种分离使Web性能优化从纯前端维度扩展到服务端渲染阶段,减少了客户端JavaScript包体积。React框架通过这种模型重新定义了组件渲染的边界。
服务端组件的限制:
– 不能使用useState、useEffect、useReducer等客户端Hook
– 不能绑定事件监听器(onClick、onChange等)
– 不能使用浏览器API(window、document、localStorage)
– 可以使用async/await直接获取数据
客户端组件的限制:
– 不能直接导入服务端组件,但可以通过children prop传递
– 增加客户端JS包体积
– 数据获取需要通过API或Server Action
服务端组件数据获取与渲染
服务端组件最大的优势是直接在组件内执行异步数据获取,无需额外API层。以下是一个商品列表页的RSC实现:
// app/products/page.tsx (Server Component)
import { db } from '@/lib/database'
import ProductCard from './ProductCard'
import { Suspense } from 'react'
async function getProducts(category: string) {
const products = await db.product.findMany({
where: { category },
orderBy: { sales: 'desc' },
take: 20
})
return products
}
export default async function ProductsPage({
searchParams
}: {
searchParams: { category: string }
}) {
const category = searchParams.category || 'all'
return (
<div>
<h1>{category} 商品列表</h1>
<Suspense fallback={<ProductSkeleton />}>
<ProductList category={category} />
</Suspense>
</div>
)
}
async function ProductList({ category }: { category: string }) {
const products = await getProducts(category)
return (
<div className="grid grid-cols-4 gap-4">
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
)
}
Suspense包裹异步组件,在数据加载期间显示骨架屏。服务端组件通过async函数直接查询数据库,无需在前端发起fetch请求。searchParams作为props传递到服务端组件,实现基于URL参数的数据获取。
客户端组件交互与Server Actions
纯展示型组件作为服务端组件,交互型组件标记为客户端组件。组件库设计时需要明确每个组件的运行环境。以下是一个购物车交互组件:
// app/components/AddToCart.tsx
'use client'
import { useState, useTransition } from 'react'
import { addToCartAction } from './actions'
export default function AddToCart({ productId }: { productId: string }) {
const [quantity, setQuantity] = useState(1)
const [isPending, startTransition] = useTransition()
return (
<div>
<input
type="number"
value={quantity}
min={1}
onChange={(e) => setQuantity(Number(e.target.value))}
/>
<button
disabled={isPending}
onClick={() => startTransition(async () => {
await addToCartAction(productId, quantity)
})}
>
{isPending ? '添加中...' : '加入购物车'}
</button>
</div>
)
}
Server Actions定义在服务端,客户端通过引用调用,无需手写API路由:
// app/components/actions.ts
'use server'
import { db } from '@/lib/database'
import { auth } from '@/lib/auth'
import { revalidatePath } from 'next/cache'
export async function addToCartAction(productId: string, quantity: number) {
const session = await auth()
if (!session) throw new Error('未登录')
await db.cartItem.upsert({
where: {
userId_productId: {
userId: session.user.id,
productId
}
},
update: { quantity: { increment: quantity } },
create: { userId: session.user.id, productId, quantity }
})
revalidatePath('/cart')
}
Server Actions中调用revalidatePath刷新缓存,购物车页面数据自动更新。整个交互流程不涉及手写API端点,前端工程化的复杂度明显降低。
服务端与客户端组件组合模式
服务端组件不能直接导入客户端组件中作为子组件,但可以通过children或props传递。这个限制确保了组件的渲染位置明确:
// layout.tsx (Server Component)
import { db } from '@/lib/database'
import { Filter } from './Filter' // Client Component
import { ProductTable } from './ProductTable' // Server Component
export default async function Layout({ children }) {
const categories = await db.category.findMany()
return (
<div className="flex">
{/* Client Component接收服务端数据作为props */}
<Filter categories={categories} />
{/* Server Component直接渲染 */}
<main>
{children}
</main>
</div>
)
}
// 在客户端组件中通过children插入服务端组件
// <ClientWrapper>
// <ServerComponent /> {/* 作为children传入,不触发客户端导入 */}
// </ClientWrapper>
流式渲染与性能优化
响应式布局场景下,RSC的流式渲染能力使首屏内容逐步呈现。配合loading.tsx和error.tsx约定文件,实现路由级加载和错误边界:
// app/products/loading.tsx
export default function Loading() {
return (
<div className="grid grid-cols-4 gap-4 animate-pulse">
{Array.from({ length: 8 }).map((_, i) => (
<div key={i} className="h-64 bg-gray-200 rounded" />
))}
</div>
)
}
// app/products/error.tsx
'use client'
export default function Error({ error, reset }: {
error: Error
reset: () => void
}) {
return (
<div>
<p>加载失败: {error.message}</p>
<button onClick={reset}>重试</button>
</div>
)
}
跨端小程序开发场景中,RSC的思路同样适用:将数据获取和业务逻辑保留在服务端,客户端仅处理UI渲染和交互。组件渲染模式的演进方向是减少客户端负担,将计算密集型操作回归服务端。TypeScript实战中,RSC的类型推导更加完整,因为服务端组件返回的是ReactNode而非Promise,编译器能够提供更精确的类型检查。前端工程化的未来方向是将渲染策略从框架层面固化,开发者只需声明组件运行环境,框架自动处理数据流和代码分割。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/reactservercomponents-shi-zhan-fu-wu-duan-zu-jian-xuan-ran/