关于 server-components
介绍 Next.js App Router 中 React Server Components 的开发技能,帮助开发者区分服务端与客户端组件,并组织数据获取、交互和流式渲染。
主要功能
- 01服务端与客户端组件用法
- 02服务端数据传递至客户端
- 03children 与插槽组合模式
- 04异步及并行数据获取
- 05使用 Suspense 流式渲染
适用场景
- 01确定 Next.js 应用的组件边界
- 02获取后端数据并将敏感信息留在服务端
- 03为服务端渲染页面添加交互
- 04流式呈现耗时的页面区块
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill server-components安装后在 Claude Code 中输入/server-components确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/data-fetching-patterns.mdreferences/composition-patterns.mdserver-vs-client.md详细介绍
Next.js 中的 React Server Components
概述
React Server Components(RSC)允许组件在服务器上渲染,从而减少客户端 JavaScript,并支持直接访问数据。在 Next.js App Router 中,所有组件默认都是 Server Components。
Server Components 与 Client Components
Server Components(默认)
Server Components 仅在服务器上运行:
// app/users/page.tsx (Server Component - default)
async function UsersPage() {
const users = await db.user.findMany() // Direct DB access
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
优点:
- 直接访问数据库/文件系统
- 将敏感数据保留在服务器上(API 密钥、令牌)
- 减小客户端代码包体积
- 自动代码分割
Client Components
添加 '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: {count}
</button>
)
}
在以下情况下使用 Client Components:
- 使用
useState、useEffect、useReducer - 使用事件处理器(
onClick、onChange) - 使用浏览器 API(
window、document) - 使用带状态的自定义 Hook
心智模型
可以将组件树看作具有一个“客户端边界”:
Server Component (page.tsx)
├── Server Component (header.tsx)
├── Client Component ('use client') ← boundary
│ ├── Client Component (child)
│ └── Client Component (child)
└── Server Component (footer.tsx)
关键规则:
- Server Components 可以导入 Client Components
- Client Components 不能导入 Server Components
- 可以将 Server Components 作为
children传递给 Client Components
组合模式
模式 1:服务器数据 → 客户端交互
在 Server Component 中获取数据,然后传递给 Client Component:
// app/products/page.tsx (Server)
import { ProductList } from './product-list'
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
// app/products/product-list.tsx (Client)
'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:将子组件作为 Server Components
通过 children 属性传递 Server Components:
// components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children} {/* Server Component content */}
</div>
)
}
// app/page.tsx (Server)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'
export default function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* Renders on server! */}
</ClientWrapper>
)
}
模式 3:用于复杂布局的插槽
使用多个 children 插槽:
// 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>
)
}
数据获取
异步 Server Components
Server Components 可以是异步的:
// 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 /> {/* Renders immediately */}
<Suspense fallback={<PostsSkeleton />}>
<SlowPosts /> {/* Streams when ready */}
</Suspense>
</div>
)
}
选择指南
在以下情况下使用 Server Component:
- 获取数据
- 访问后端资源
- 将敏感信息保留在服务器上
- 减少客户端 JavaScript
- 组件没有交互功能
在以下情况下使用 Client Component:
- 使用状态(
useState、useReducer) - 使用副作用(
useEffect) - 使用事件监听器
- 使用浏览器 API
- 使用带状态的自定义 Hook
常见错误
- 不要不必要地添加
'use client'——这会增大代码包体积 - 不要尝试在 Client Components 中导入 Server Components
- 应当在边界处序列化数据(不包含函数、类或日期)
- 应当使用 children 模式进行组合
资源
如需了解详细模式,请参阅:
references/server-vs-client.md- 完整对比指南references/composition-patterns.md- 高级组合examples/data-fetching-patterns.md- 数据获取示例
评论
支持 Markdown