React Server Components(RSC)是 React 和 Next.js 的重大架构变革—— 服务端直接渲染组件,无需客户端 JavaScript。 Claude Code 能帮你理解 RSC 的心智模型,生成正确的服务端/客户端组件代码。
RSC 核心心智模型
传统 React(纯客户端):
所有组件 → 发送 JS 到浏览器 → 浏览器执行渲染
React Server Components(RSC):
服务端组件 → 服务器执行渲染 → 发送 HTML(无 JS)
客户端组件 → 仍然发送 JS → 浏览器执行
好处:
减少客户端 JS 体积(大型依赖不发到浏览器)
直接访问数据库/文件系统(在服务器上)
更快的首屏渲染(HTML 直接返回)
服务端 vs 客户端组件选择
让 Claude Code 帮你判断:
Prompt:
这些组件应该是服务端还是客户端组件?
帮我用注释标注并解释原因:
[粘贴组件列表]
Claude Code 的判断原则(会解释给你):
typescript
// 服务端组件(默认,不加 'use client'):
// ✅ 直接读数据库
// ✅ 访问文件系统
// ✅ 使用 API secrets(不暴露给客户端)
// ✅ 大型依赖(只在服务器执行,不发到浏览器)
// ❌ 不能用 useState/useEffect
// ❌ 不能有事件处理(onClick 等)
// 客户端组件(需要 'use client' 指令):
// ✅ 需要 useState/useEffect/useRef
// ✅ 需要事件处理(onClick/onChange)
// ✅ 需要浏览器 API(window/localStorage)
// ✅ 需要实时更新的 UI场景一:数据获取页面(Server Component)
Prompt:
帮我写一个 Next.js 15 的博客文章列表页面,
直接在服务端从数据库获取数据,
支持分页和搜索(URL 参数),
不要用 useEffect 或 SWR。
Claude Code 生成:
typescript
// app/blog/page.tsx(服务端组件,默认)
import { db } from '@/lib/db'
interface PageProps {
searchParams: Promise<{ page?: string; q?: string }>
}
export default async function BlogPage({ searchParams }: PageProps) {
const { page = '1', q = '' } = await searchParams
const pageNum = parseInt(page)
const pageSize = 10
// 直接在服务端查数据库(不需要 API 路由)
const { articles, total } = await db.article.findMany({
where: q ? { title: { contains: q } } : {},
skip: (pageNum - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: 'desc' }
})
return (
<div>
<SearchForm defaultValue={q} /> {/* 客户端组件 */}
<ArticleList articles={articles} /> {/* 可以是服务端组件 */}
<Pagination
current={pageNum}
total={Math.ceil(total / pageSize)}
/>
</div>
)
}
// 生成静态参数(可选,用于 SSG)
export async function generateMetadata({ searchParams }: PageProps) {
const { q } = await searchParams
return {
title: q ? `搜索"${q}" - 博客` : '博客文章列表'
}
}场景二:交互组件(Client Component)
Prompt:
帮我写一个点赞按钮组件,
点击时调用 Server Action 更新数据库,
显示实时点赞数(乐观更新)。
要区分服务端和客户端部分。
typescript
// components/LikeButton.tsx
'use client' // 需要交互,必须是客户端组件
import { useState, useTransition } from 'react'
import { toggleLike } from '@/actions/like' // Server Action
interface LikeButtonProps {
articleId: string
initialLikes: number
initialLiked: boolean
}
export function LikeButton({ articleId, initialLikes, initialLiked }: LikeButtonProps) {
const [likes, setLikes] = useState(initialLikes)
const [liked, setLiked] = useState(initialLiked)
const [isPending, startTransition] = useTransition()
function handleClick() {
// 乐观更新:立即更新 UI
setLiked(!liked)
setLikes(prev => liked ? prev - 1 : prev + 1)
// 后台同步到服务器
startTransition(async () => {
const result = await toggleLike(articleId)
if (!result.success) {
// 回滚乐观更新
setLiked(liked)
setLikes(initialLikes)
}
})
}
return (
<button
onClick={handleClick}
disabled={isPending}
className={liked ? 'text-red-500' : 'text-gray-400'}
>
{liked ? '❤️' : '🤍'} {likes}
</button>
)
}
// actions/like.ts(Server Action)
'use server'
import { db } from '@/lib/db'
import { auth } from '@/lib/auth'
export async function toggleLike(articleId: string) {
const session = await auth()
if (!session) return { success: false, error: 'Unauthorized' }
// 直接操作数据库(在服务器上执行)
const existing = await db.like.findUnique({
where: { userId_articleId: { userId: session.user.id, articleId } }
})
if (existing) {
await db.like.delete({ where: { id: existing.id } })
} else {
await db.like.create({ data: { userId: session.user.id, articleId } })
}
return { success: true }
}场景三:Streaming(流式渲染)
Prompt:
帮我实现一个页面,主要内容快速加载,
慢速数据(如推荐列表,需要 AI 计算)用 Suspense 流式加载,
不阻塞整个页面的展示。
typescript
// app/article/[id]/page.tsx
import { Suspense } from 'react'
import { ArticleContent } from './ArticleContent'
import { RecommendedArticles } from './RecommendedArticles'
export default function ArticlePage({ params }: { params: { id: string } }) {
return (
<div>
{/* 主内容:快速加载 */}
<ArticleContent id={params.id} />
{/* 推荐内容:异步加载,不阻塞主内容 */}
<Suspense fallback={<RecommendSkeleton />}>
<RecommendedArticles articleId={params.id} />
</Suspense>
</div>
)
}
// RecommendedArticles.tsx(服务端组件,支持异步)
async function RecommendedArticles({ articleId }: { articleId: string }) {
// 慢速 AI 推荐计算,不影响主内容渲染
const recommendations = await getAIRecommendations(articleId) // 可能 2-3 秒
return <ArticleList articles={recommendations} />
}常见 RSC 错误排查
Claude Code 能快速诊断这类错误:
错误1:
"You're importing a component that needs useState.
It only works in a Client Component."
→ 解决:在组件文件顶部添加 'use client'
错误2:
"Event handlers cannot be passed to Client Component props."
→ 解决:不要从服务端组件传递函数给客户端组件
改为在客户端组件内定义 handler
错误3:
Hydration failed because the server rendered HTML didn't match
→ 解决:检查是否在服务端使用了浏览器API(window/Date.now())
用 useEffect 或 suppressHydrationWarning 处理
来源:Next.js 官方文档 + React 官方文档