React Server Components(RSC)是React 18引入的架构特性,允许组件在服务端渲染并直接流式传输到客户端。与SSR不同,RSC组件的代码不会打包到客户端bundle中,从根本上减少JavaScript体积。Next.js App Router是RSC的主要落地框架。本文围绕RSC的数据加载、组件拆分、客户端交互边界,给出可运行的代码实践。
Server Component与Client Component的边界划分
App Router中默认所有组件都是Server Component。文件顶部添加"use client"指令标记Client Component。两者的核心区别:
- Server Component:服务端渲染,可用数据库和文件系统API,不打包到客户端bundle
- Client Component:客户端渲染,可用useState/useEffect和事件监听,打包到客户端
边界划分原则:将数据获取、静态渲染放在Server Component,将交互逻辑(状态管理、事件处理、浏览器API)隔离到Client Component叶子节点。
// app/products/page.tsx (Server Component)
import { db } from '@/lib/db'
import { ProductList } from '@/components/ProductList'
import { SearchBar } from '@/components/SearchBar'
export default async function ProductsPage() {
const products = await db.product.findMany({
include: { category: true },
orderBy: { createdAt: 'desc' },
take: 20,
})
return (
<main>
<h1>产品列表</h1>
<SearchBar /> {/* Client Component */}
<ProductList products={products} /> {/* Server Component */}
</main>
)
}
Server Component中直接执行数据库查询,无需API层。数据获取在服务端完成,减少了客户端到服务器的往返请求。
Server Component数据加载与流式渲染
RSC支持React Suspense实现流式渲染,页面可分块传输,优先加载的内容先呈现给用户:
// app/dashboard/page.tsx
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<main>
<h1>控制台</h1>
<Suspense fallback={<div>加载统计数据...</div>}>
<StatsSection />
</Suspense>
<Suspense fallback={<div>加载最近活动...</div>}>
<RecentActivity />
</Suspense>
</main>
)
}
async function StatsSection() {
const stats = await fetch('https://api.example.com/stats', {
next: { revalidate: 60 } // ISR: 每60秒重新验证
}).then(r => r.json())
return (
<div className="grid grid-cols-3 gap-4">
<StatCard label="总用户" value={stats.totalUsers} />
<StatCard label="活跃用户" value={stats.activeUsers} />
<StatCard label="今日订单" value={stats.todayOrders} />
</div>
)
}
next: { revalidate: 60 }配置增量静态再生(ISR),数据缓存60秒后自动重新生成。fetch在Server Component中默认启用缓存,可通过cache: 'no-store'禁用。
Client Component状态管理与Server Component通信
当需要用户交互触发数据更新时,Client Component通过Server Actions调用服务端逻辑:
// components/LikeButton.tsx
'use client'
import { useTransition } from 'react'
import { likePost } from '@/actions/posts'
export function LikeButton({ postId, initialLikes }: { postId: number; initialLikes: number }) {
const [isPending, startTransition] = useTransition()
return (
<button
onClick={() => startTransition(() => likePost(postId))}
disabled={isPending}
>
{initialLikes} 赞 {isPending && '...'}
</button>
)
}
// actions/posts.ts
'use server'
import { db } from '@/lib/db'
import { revalidatePath } from 'next/cache'
export async function likePost(postId: number) {
await db.post.update({
where: { id: postId },
data: { likes: { increment: 1 } },
})
revalidatePath(`/posts/${postId}`) // 触发对应路径的RSC重新渲染
}
Server Actions通过revalidatePath触发缓存失效,Next.js自动重新渲染对应的Server Component,新的HTML流式传输到客户端。整个过程无需编写API端点,数据变更和UI更新在框架层面自动协调。
性能优化:减少Client Bundle体积
RSC的核心价值是减少客户端JavaScript体积。实测对比:
// 方案1: 传统CSR——全部打包到客户端
'use client'
import { format } from 'date-fns' // 67KB
import { Chart } from 'chart.js' // 200KB
import { marked } from 'marked' // 45KB
export function Report({ data }) {
const html = marked(data.content)
const date = format(data.date, 'yyyy-MM-dd')
return <Chart data={data} />
}
// 客户端额外加载: ~312KB
// 方案2: RSC拆分——格式化和解析在服务端完成
// app/report/page.tsx (Server Component)
import { format } from 'date-fns'
import { marked } from 'marked'
import { ClientChart } from '@/components/ClientChart'
export default async function ReportPage({ params }) {
const data = await db.report.findUnique({ where: { id: params.id } })
const html = marked(data.content) // 服务端解析,客户端不加载marked
const date = format(data.date, 'yyyy-MM-dd') // 服务端格式化
return (
<article>
<h1>{data.title}</h1>
<time>{date}</time>
<div dangerouslySetInnerHTML={{ __html: html }} />
<ClientChart data={data.metrics} /> {/* 仅图表组件打包到客户端 */}
</article>
)
}
// 客户端额外加载: ~200KB (仅Chart.js)
date-fns和marked的代码不会出现在客户端bundle中。对于内容密集型页面(博客、文档站、管理后台),RSC可将客户端JS体积减少60-80%,首屏交互时间(TTI)显著改善。
React Server Components不是SSR的替代,而是补充。SSR解决首屏HTML渲染,RSC解决长期运行时的JavaScript体积问题。两者在App Router中协同工作:RSC生成初始HTML,客户端水合后,交互部分由Client Component接管。掌握Server与Client组件的边界划分,是构建高性能React应用的关键技能。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/reactservercomponents-shi-zhan-fu-wu-duan-xuan-ran-xing/