引言
React Server Components (RSC) 是 React 18 引入的革命性特性,在 React 19 中成为稳定功能。它允许组件在服务器端渲染,减少客户端 JavaScript,提升性能。
官方资料来源:
一、什么是 Server Components
1.1 核心概念
Server Components 是在服务器端执行的 React 组件,它们:
- 不会发送到客户端:减少 bundle 大小
- 可以直接访问后端资源:数据库、文件系统、API
- 支持 async/await:直接在组件中异步获取数据
1.2 Server Components vs Client Components
| 特性 | Server Components | Client Components |
|---|---|---|
| 执行位置 | 服务器 | 浏览器 |
| 可以使用 Hooks | ❌ | ✅ |
| 可以访问后端 | ✅ | ❌ |
| 可以使用浏览器 API | ❌ | ✅ |
| 可以有交互 | ❌ | ✅ |
| Bundle 大小 | 0 | 计入 |
二、基础用法
2.1 创建 Server Component
jsx
// app/blog/[id]/page.jsx (Server Component)
import { db } from '@/lib/db';
export default async function BlogPost({ params }) {
// 直接在组件中查询数据库
const post = await db.posts.findById(params.id);
return (
<article>
<h1>{post.title}</h1>
<p>{post.author}</p>
<div>{post.content}</div>
</article>
);
}
关键点:
- 函数是
async的 - 直接使用
await获取数据 - 不需要
useEffect或useState
2.2 创建 Client Component
jsx
// components/LikeButton.jsx (Client Component)
'use client'; // 标记为 Client Component
import { useState } from 'react';
export default function LikeButton({ postId }) {
const [likes, setLikes] = useState(0);
const handleLike = async () => {
await fetch(`/api/like/${postId}`, { method: 'POST' });
setLikes(likes + 1);
};
return (
<button onClick={handleLike}>
❤️ {likes}
</button>
);
}
关键点:
- 文件顶部添加
'use client' - 可以使用 Hooks 和事件处理
2.3 组合使用
jsx
// app/blog/[id]/page.jsx (Server Component)
import { db } from '@/lib/db';
import LikeButton from '@/components/LikeButton'; // Client Component
export default async function BlogPost({ params }) {
const post = await db.posts.findById(params.id);
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
{/* Client Component 嵌入 Server Component */}
<LikeButton postId={post.id} />
</article>
);
}
三、数据获取模式
3.1 并行数据获取
jsx
// ❌ 串行获取(慢)
export default async function Dashboard() {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
const comments = await fetchComments(user.id);
return <div>...</div>;
}
// ✅ 并行获取(快)
export default async function Dashboard() {
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return <div>...</div>;
}
3.2 流式渲染(Streaming)
jsx
import { Suspense } from 'react';
export default function Page() {
return (
<div>
<h1>Dashboard</h1>
{/* 立即显示 */}
<UserInfo />
{/* 异步加载,显示 loading */}
<Suspense fallback={<Skeleton />}>
<Posts />
</Suspense>
<Suspense fallback={<Skeleton />}>
<Comments />
</Suspense>
</div>
);
}
async function Posts() {
const posts = await fetchPosts(); // 慢查询
return <div>{posts.map(...)}</div>;
}
优势:
- 页面立即显示
- 慢查询不阻塞快内容
- 更好的用户体验
3.3 预加载数据
jsx
// app/blog/[id]/page.jsx
import { preload } from '@/lib/data';
export default async function BlogPost({ params }) {
// 预加载数据(不等待)
preload(params.id);
// 实际获取数据
const post = await fetchPost(params.id);
return <article>{post.content}</article>;
}
// lib/data.js
import { cache } from 'react';
export const fetchPost = cache(async (id) => {
return await db.posts.findById(id);
});
export const preload = (id) => {
void fetchPost(id); // 触发缓存
};
四、性能优化
4.1 使用 React Cache
jsx
import { cache } from 'react';
// 缓存数据库查询
export const getUser = cache(async (id) => {
return await db.users.findById(id);
});
// 多次调用只执行一次
export default async function Page() {
const user1 = await getUser(1); // 执行查询
const user2 = await getUser(1); // 使用缓存
return <div>...</div>;
}
4.2 代码分割
jsx
// Server Component 自动代码分割
import HeavyComponent from './HeavyComponent'; // 不会打包到客户端
export default async function Page() {
const data = await fetchData();
// HeavyComponent 只在服务器执行
return <HeavyComponent data={data} />;
}
4.3 减少客户端 JavaScript
jsx
// ❌ 不好:整个库打包到客户端
'use client';
import { format } from 'date-fns';
export default function Post({ date }) {
return <time>{format(date, 'PPP')}</time>;
}
// ✅ 好:在服务器格式化
import { format } from 'date-fns';
export default function Post({ date }) {
const formatted = format(date, 'PPP');
return <time>{formatted}</time>;
}
五、常见模式
5.1 布局组件
jsx
// app/layout.jsx (Server Component)
export default async function RootLayout({ children }) {
const user = await getCurrentUser();
return (
<html>
<body>
<Header user={user} />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
5.2 数据提供者模式
jsx
// app/dashboard/layout.jsx
export default async function DashboardLayout({ children }) {
const user = await getUser();
return (
<div>
<Sidebar user={user} />
<main>{children}</main>
</div>
);
}
// app/dashboard/page.jsx
export default async function DashboardPage() {
// 不需要重复获取 user,layout 已经获取了
const stats = await getStats();
return <div>{stats}</div>;
}
5.3 条件渲染
jsx
export default async function Page() {
const user = await getUser();
if (!user) {
return <LoginPrompt />;
}
if (!user.isPremium) {
return <UpgradePrompt />;
}
return <PremiumContent />;
}
六、最佳实践
6.1 何时使用 Server Components
适合:
- 数据获取
- 访问后端资源
- 敏感信息处理(API keys)
- 大型依赖库(只在服务器使用)
不适合:
- 需要交互(onClick, onChange)
- 需要浏览器 API(localStorage, window)
- 需要 Hooks(useState, useEffect)
6.2 何时使用 Client Components
适合:
- 交互式 UI(按钮、表单)
- 使用浏览器 API
- 使用 React Hooks
- 第三方库需要客户端
6.3 组件边界
jsx
// ❌ 不好:整个页面都是 Client Component
'use client';
export default function Page() {
const [count, setCount] = useState(0);
return (
<div>
<Header /> {/* 不需要交互,但被迫成为 Client Component */}
<button onClick={() => setCount(count + 1)}>{count}</button>
<Footer /> {/* 不需要交互,但被迫成为 Client Component */}
</div>
);
}
// ✅ 好:只有需要交互的部分是 Client Component
export default function Page() {
return (
<div>
<Header /> {/* Server Component */}
<Counter /> {/* Client Component */}
<Footer /> {/* Server Component */}
</div>
);
}
// components/Counter.jsx
'use client';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
七、实战案例
7.1 博客系统
jsx
// app/blog/[slug]/page.jsx
import { db } from '@/lib/db';
import { Suspense } from 'react';
import CommentList from '@/components/CommentList';
import CommentForm from '@/components/CommentForm';
export default async function BlogPost({ params }) {
// 并行获取数据
const [post, author] = await Promise.all([
db.posts.findBySlug(params.slug),
db.users.findById(post.authorId)
]);
return (
<article>
<h1>{post.title}</h1>
<p>作者:{author.name}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
{/* 评论列表(流式加载) */}
<Suspense fallback={<div>加载评论中...</div>}>
<CommentList postId={post.id} />
</Suspense>
{/* 评论表单(Client Component) */}
<CommentForm postId={post.id} />
</article>
);
}
// 生成静态路径
export async function generateStaticParams() {
const posts = await db.posts.findAll();
return posts.map(post => ({ slug: post.slug }));
}
7.2 电商产品页
jsx
// app/products/[id]/page.jsx
export default async function ProductPage({ params }) {
const product = await fetchProduct(params.id);
return (
<div>
<ProductImages images={product.images} /> {/* Client Component */}
<ProductInfo product={product} /> {/* Server Component */}
<Suspense fallback={<Skeleton />}>
<RelatedProducts categoryId={product.categoryId} />
</Suspense>
<Suspense fallback={<Skeleton />}>
<Reviews productId={product.id} />
</Suspense>
</div>
);
}
八、常见问题
Q1: Server Components 可以导入 Client Components 吗?
✅ 可以
jsx
// Server Component
import ClientButton from './ClientButton'; // ✅
export default function Page() {
return <ClientButton />;
}
Q2: Client Components 可以导入 Server Components 吗?
❌ 不可以直接导入,但可以通过 children 传递
jsx
// ❌ 不行
'use client';
import ServerComponent from './ServerComponent';
export default function ClientComponent() {
return <ServerComponent />; // 错误
}
// ✅ 可以
'use client';
export default function ClientComponent({ children }) {
return <div>{children}</div>;
}
// 使用
<ClientComponent>
<ServerComponent /> {/* 作为 children 传递 */}
</ClientComponent>
Q3: 如何在 Server Component 中使用环境变量?
jsx
// ✅ 直接使用(不会暴露给客户端)
export default async function Page() {
const apiKey = process.env.SECRET_API_KEY;
const data = await fetch(`https://api.example.com?key=${apiKey}`);
return <div>{data}</div>;
}
总结
React Server Components 的核心优势:
-
性能提升
- 减少客户端 JavaScript
- 更快的首屏加载
- 自动代码分割
-
开发体验
- 直接访问后端资源
- 简化数据获取
- 更好的类型安全
-
最佳实践
- Server Components 处理数据
- Client Components 处理交互
- 合理划分组件边界
参考资料: