name: server-components description: 当用户询问关于“服务器组件”、“客户端组件”、“'use client’指令”、“何时使用服务器与客户端”、“RSC模式”、“组件组合”、“组件中的数据获取”,或需要指导Next.js中的React服务器组件架构时,应使用此技能。 version: 1.0.0
Next.js中的React服务器组件
概述
React服务器组件(RSC)允许组件在服务器上渲染,减少客户端JavaScript并启用直接数据访问。在Next.js App Router中,默认所有组件都是服务器组件。
服务器与客户端组件
服务器组件(默认)
服务器组件仅在服务器上运行:
// app/users/page.tsx (服务器组件 - 默认)
async function UsersPage() {
const users = await db.user.findMany() // 直接数据库访问
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
好处:
- 直接数据库/文件系统访问
- 将敏感数据保留在服务器上(API密钥、令牌)
- 减少客户端包大小
- 自动代码拆分
客户端组件
添加 'use client' 指令以启用交互性:
// components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
计数: {count}
</button>
)
}
使用客户端组件用于:
useState,useEffect,useReducer- 事件处理程序 (
onClick,onChange) - 浏览器API (
window,document) - 带有状态的自定义钩子
心智模型
将组件树视为具有“客户端边界”:
服务器组件 (page.tsx)
├── 服务器组件 (header.tsx)
├── 客户端组件 ('use client') ← 边界
│ ├── 客户端组件 (子组件)
│ └── 客户端组件 (子组件)
└── 服务器组件 (footer.tsx)
关键规则:
- 服务器组件可以导入客户端组件
- 客户端组件不能导入服务器组件
- 您可以将服务器组件作为
children传递给客户端组件
组合模式
模式1:服务器数据 → 客户端交互性
在服务器组件中获取数据,传递给客户端:
// app/products/page.tsx (服务器)
import { ProductList } from './product-list'
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
// app/products/product-list.tsx (客户端)
'use client'
export function ProductList({ products }: { products: Product[] }) {
const [filter, setFilter] = useState('')
const filtered = products.filter(p =>
p.name.includes(filter)
)
return (
<>
<input onChange={e => setFilter(e.target.value)} />
{filtered.map(p => <ProductCard key={p.id} product={p} />)}
</>
)
}
模式2:子组件作为服务器组件
通过children属性传递服务器组件:
// components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>切换</button>
{isOpen && children} {/* 服务器组件内容 */}
</div>
)
}
// app/page.tsx (服务器)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'
export default function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* 在服务器上渲染! */}
</ClientWrapper>
)
}
模式3:用于复杂布局的插槽
使用多个子组件插槽:
// components/dashboard-shell.tsx
'use client'
interface Props {
sidebar: React.ReactNode
main: React.ReactNode
}
export function DashboardShell({ sidebar, main }: Props) {
const [collapsed, setCollapsed] = useState(false)
return (
<div className="flex">
{!collapsed && <aside>{sidebar}</aside>}
<main>{main}</main>
</div>
)
}
数据获取
异步服务器组件
服务器组件可以是异步的:
// app/posts/page.tsx
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json())
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
并行数据获取
并行获取多个资源:
export default async function DashboardPage() {
const [user, posts, analytics] = await Promise.all([
getUser(),
getPosts(),
getAnalytics(),
])
return (
<Dashboard user={user} posts={posts} analytics={analytics} />
)
}
使用Suspense流式传输
流式传输慢组件:
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header /> {/* 立即渲染 */}
<Suspense fallback={<PostsSkeleton />}>
<SlowPosts /> {/* 准备就绪时流式传输 */}
</Suspense>
</div>
)
}
决策指南
使用服务器组件当:
- 获取数据
- 访问后端资源
- 将敏感信息保留在服务器上
- 减少客户端JavaScript
- 组件没有交互性
使用客户端组件当:
- 使用状态 (
useState,useReducer) - 使用效果 (
useEffect) - 使用事件监听器
- 使用浏览器API
- 使用带有状态的自定义钩子
常见错误
- 不要不必要地添加
'use client'- 它会增加包大小 - 不要尝试将服务器组件导入客户端组件
- 要在边界处序列化数据(没有函数、类或日期)
- 要使用子组件模式进行组合
资源
有关详细模式,请参阅:
references/server-vs-client.md- 完整比较指南references/composition-patterns.md- 高级组合examples/data-fetching-patterns.md- 数据获取示例