服务器组件Skill server-components

此技能详细介绍了React服务器组件在Next.js中的应用,涵盖服务器与客户端组件的区分、组合模式及数据获取方法,适用于前端开发。关键词:React服务器组件、Next.js、服务器渲染、客户端交互、组件架构。

前端开发 0 次安装 0 次浏览 更新于 3/8/2026

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)

关键规则:

  1. 服务器组件可以导入客户端组件
  2. 客户端组件不能导入服务器组件
  3. 您可以将服务器组件作为 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
  • 使用带有状态的自定义钩子

常见错误

  1. 不要不必要地添加 'use client' - 它会增加包大小
  2. 不要尝试将服务器组件导入客户端组件
  3. 在边界处序列化数据(没有函数、类或日期)
  4. 使用子组件模式进行组合

资源

有关详细模式,请参阅:

  • references/server-vs-client.md - 完整比较指南
  • references/composition-patterns.md - 高级组合
  • examples/data-fetching-patterns.md - 数据获取示例