React Server Components

RSC 的概念、Server 与 Client 组件的分工、Suspense 与流式渲染机制。

#tech / dev / frame #resource / react #type / concept #status / growing

[!info] related notes

React Server Components

定义

React Server Components (RSC) 是一种组件模型,允许部分组件只在服务端执行,其渲染结果(序列化的组件树)被流式发送到客户端。客户端组件仍然在浏览器中运行并拥有交互能力。

核心区别:

  • Server Component:在服务端运行,可以访问文件系统、数据库等,不发送 JS 到客户端
  • Client Component:在客户端运行,可以使用 Hooks、浏览器 API,有交互能力

为什么需要 RSC

传统 SSR 的问题:

  • 服务端渲染 HTML 后,客户端需要下载所有 JS 并”hydrate”整个应用
  • 即使某些组件不需要交互(如文章内容),其 JS 仍然被发送和执行
  • 数据获取通常要在客户端重新执行

RSC 的优势:

  • Server Component 的 JS 不发送到客户端,减少 bundle 大小
  • 可以直接在组件内 async/await 获取数据,不需要 useEffect
  • 数据获取和渲染在同一层(服务端),避免客户端-服务端往返

Server vs Client 组件

Server Component(默认)

// app/article/page.tsx (Next.js App Router)
// 没有 "use client" 指令 = Server Component

import { db } from '@/lib/db'

export default async function ArticlePage({ params }: { params: { id: string } }) {
  // 可以直接访问数据库
  const article = await db.article.findUnique({
    where: { id: params.id }
  })

  return (
    <article>
      <h1>{article.title}</h1>
      <p>{article.content}</p>
      {/* 引入 Client Component 处理交互 */}
      <LikeButton articleId={article.id} />
    </article>
  )
}

特点:

  • 默认就是 Server Component
  • 可以是 async 函数
  • 可以直接访问服务端资源
  • 不能使用 useState、useEffect 等 Hooks
  • 不能使用浏览器 API
  • 不能使用事件处理函数(onClick 等)

Client Component

"use client" // 必须显式声明

import { useState } from 'react'

export function LikeButton({ articleId }: { articleId: string }) {
  const [liked, setLiked] = useState(false)

  return (
    <button onClick={() => {
      setLiked(!liked)
      fetch(`/api/articles/${articleId}/like`, { method: 'POST' })
    }}>
      {liked ? '已点赞' : '点赞'}
    </button>
  )
}

特点:

  • 必须在文件顶部加 "use client" 指令
  • 可以使用所有 Hooks
  • 可以使用浏览器 API
  • 可以使用事件处理函数
  • 不能直接访问服务端资源

组合规则

  • Server Component 可以导入 Client Component
  • Client Component 不能导入 Server Component(但可以通过 children 接收)
// Server Component 可以把 Server Component 作为 children 传给 Client Component
// app/layout.tsx
import { ClientLayout } from './ClientLayout'
import { ServerSidebar } from './ServerSidebar'

export default function Layout({ children }) {
  return (
    <ClientLayout sidebar={<ServerSidebar />}>
      {children}
    </ClientLayout>
  )
}

Suspense 与流式渲染

Suspense

Suspense 允许组件”等待”某些操作完成后再渲染,期间显示 fallback。

import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <h1>我的页面</h1>
      <Suspense fallback={<div>加载评论中...</div>}>
        <Comments /> {/* 这个组件可能需要异步加载 */}
      </Suspense>
    </div>
  )
}

流式渲染

RSC 支持流式发送:页面的静态部分先到达客户端并显示,动态部分(被 Suspense 包裹的部分)完成后逐步流入。

客户端接收到的内容流:

1. 立即到达:<h1>我的页面</h1> + fallback "加载评论中..."
2. 评论加载完成后:<Comments /> 替换 fallback

这比传统 SSR(等所有数据准备好再一次性发送 HTML)的体验更好。

RSC 与传统 SSR 的区别

传统 SSRRSC
渲染位置服务端渲染 HTML,客户端 hydrateServer Component 在服务端,Client Component 在客户端
JS 发送所有组件的 JS 都发送到客户端只有 Client Component 的 JS 发送到客户端
数据获取通常需要在客户端重新获取Server Component 可以直接获取,结果序列化发送
交互hydrate 后才能交互Client Component 独立交互,不阻塞

边界与常见误区

  • "use client" 不是说组件只在客户端运行,而是说它需要在客户端执行(需要 JS)
  • Server Component 不能使用任何 Hooks 或浏览器 API
  • 不要把 Server Component 看成”不需要的优化”,它是默认行为
  • RSC 不是取代 SSR,而是和 SSR 互补(SSR 用于首屏 HTML,RSC 用于组件级服务端渲染)
  • 在 Next.js App Router 中,所有组件默认是 Server Component

一句话记忆法

Server Component 在服务端运行、不发 JS 到客户端、可以直接拿数据;Client Component 在浏览器运行、有交互能力、需要 "use client" 声明。Suspense 让页面可以流式加载。

创建于 2026/5/27 更新于 2026/5/27