返回文章列表

React Server Components 实战指南

RSC 的渲染模型、Server/Client 组件边界、数据获取模式与 Next.js App Router 落地经验。

React Server Components(RSC)是 React 18+ 最重要的架构变革之一。它不是在 SSR 基础上加功能,而是重新定义了组件在哪里运行、数据在哪里获取、Bundle 里包含什么

核心概念:组件运行时的分离

类型运行环境能否用 useState能否访问 DB是否进入客户端 Bundle
Server Component服务端
Client Component浏览器
// app/posts/page.tsx — Server Component(默认)
import { db } from "@/lib/db";

export default async function PostsPage() {
  const posts = await db.post.findMany(); // 直接访问数据库
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
// components/LikeButton.tsx — Client Component
"use client";

import { useState } from "react";

export function LikeButton({ postId }: { postId: string }) {
  const [liked, setLiked] = useState(false);
  return (
    <button onClick={() => setLiked(!liked)}>{liked ? "已赞" : "点赞"}</button>
  );
}

数据获取模式的演进

Pages Router:  getServerSideProps → 组件渲染
App Router:    Server Component 直接 async/await → 流式渲染
Client:        SWR/React Query → 客户端缓存

RSC 的优势:

  • 零客户端 JS:Server Component 的代码不会打包到浏览器
  • 无瀑布请求:组件树中并行 fetch,而非 useEffect 链式依赖
  • 天然安全:API Key、数据库连接不会泄露到客户端

Server/Client 边界设计原则

  1. 默认 Server Component,只在需要交互时加 'use client'
  2. Client Component 不能 import Server Component,但可以接收 Server Component 作为 children
  3. 'use client' 推到叶子节点,最大化 Server 渲染范围
// 正确:Client 组件包裹 Server 组件的 children
"use client";
export function Modal({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false);
  return open ? <dialog>{children}</dialog> : null;
}

// page.tsx (Server)
<Modal>
  <ServerOnlyContent /> {/* 作为 children 传入,不违反边界 */}
</Modal>;

Streaming 与 Suspense

RSC 配合 Suspense 实现流式 HTML:

export default function DashboardPage() {
  return (
    <div>
      <Header /> {/* 立即发送 */}
      <Suspense fallback={<ChartSkeleton />}>
        <SlowChart /> {/* 异步加载,不阻塞 Header */}
      </Suspense>
    </div>
  );
}

浏览器会逐步收到 HTML 片段,用户感知的首屏时间大幅缩短。

常见陷阱

  • 在 Server Component 中使用 useEffect / useState → 编译报错
  • 在 Server Component 中传递非序列化 props(函数、Date 对象)→ 运行时错误
  • 过度使用 'use client',导致整个子树变成客户端组件 → 失去 RSC 优势

与 Vue/Nuxt 的对比视角

维度React RSCVue SSR (Nuxt)
组件粒度Server/Client 显式标记统一 SSR + hydration
数据获取组件内 asyncuseAsyncData / useFetch
Bundle 优化服务端组件零 JS全量 hydration
生态成熟度Next.js 15+ 较成熟Nuxt 3 稳定