文章中英模式
布鲁斯前端React面试题目 - Server Components & Hydration
深入解析React Server Components的概念、Hydration原理与使用案例。了解RSC如何改变前端开发模式,提升性能与用户体验,以及与传统客户端渲染的区别。
文章中英模式

懒得看文章?那就来看视频吧
React Server Components是什么?
React Server Components (RSC) 是React团队推出的新功能,让你的React组件可以在服务器上运行,而不是只在浏览器中运行。
简单来说,RSC让你可以决定:「这个组件应该在服务器上跑」或「这个组件应该在浏览器上跑」,从而结合两者的优点。
传统React vs Server Components
传统React
- 1. 所有组件都在浏览器运行
- 2. 需要下载所有JS代码
- 3. 数据获取需要额外API请求
渲染流程
Server Components
- 1. 部分组件在服务器运行
- 2. 只下载需要的JS代码
- 3. 可直接在服务器获取数据
渲染流程
注意: Server Components不等于SSR!SSR是整个应用先在服务器渲染成HTML,再在浏览器「水合」成可交互应用;而RSC可以只在服务器渲染某些组件,减少浏览器负担。
如何使用Server Components
Server Components与Client Components工作原理
架构对比
Server Components
在服务器上渲染
直接访问服务器资源
(数据库、文件系统等)
输出静态HTML和引用
Client Components
在浏览器中渲染
可使用React Hooks
(useState, useEffect等)
处理用户交互
浏览器最终接收到的内容
已渲染的HTML内容 + 引用占位符
JavaScript代码 + 初始状态
RSC Payload (特殊格式的JSON) + JavaScript Bundle (仅Client Components)
实际案例:无交互的排行榜
以下是一个典型的Server Component使用案例 - 纯显示的数据排行榜:
// LeaderboardPage.js (Server Component)
// 注意:没有"use client"指令,默认为Server Component
import { getTopUsers } from '@/lib/database';
// 这个异步组件直接在服务器上获取数据
export default async function LeaderboardPage() {
// 直接访问数据库,无需API调用
const topUsers = await getTopUsers(50);
return (
<div className="leaderboard">
<h1>本周排行榜</h1>
<div className="leaderboard-list">
{topUsers.map((user, index) => (
<div key={user.id} className="leaderboard-item">
<div className="rank">{index + 1}</div>
<div className="user-avatar">
<img src={user.avatar} alt={user.name} />
</div>
<div className="user-info">
<h3>{user.name}</h3>
<p>{user.points} 点</p>
</div>
</div>
))}
</div>
</div>
);
}优势说明:
- 1. 数据直接在服务器获取,无需额外API请求
- 2. 不需要加载状态处理逻辑,服务器已完成渲染
- 3. 浏览器不需要下载数据获取和处理的JavaScript代码
- 4. 由于没有交互功能,整个组件可保持为Server Component
- 5. SEO友好,搜索引擎可直接爬取完整内容
排行榜渲染流程图解
Server Component vs Client Component 排行榜渲染流程
Server Component
用户请求页面
服务器执行React组件
直接访问数据库/文件系统
服务器渲染HTML
返回完整HTML
单次网络请求
浏览器立即显示内容
无需等待额外请求
Client Component
用户请求页面
服务器返回基本HTML和JS
需下载大量JS代码
浏览器下载并执行JS
消耗客户端资源
组件发送API请求获取数据
额外网络请求延迟
获取数据后渲染UI
需处理加载和错误状态
两种模式对比
Server Component 优势
- 1. 减少网络请求次数
- 2. 减少客户端JS体积
- 3. 无需处理加载状态
- 4. 直接访问后端资源
- 5. SEO友好
- 6. 首次内容显示更快
Client Component 劣势
- 1. 需要额外的API请求
- 2. 较大的JS包体积
- 3. 需要处理加载状态
- 4. 需要额外的错误处理
- 5. 首次内容显示较慢
- 6. SEO不友好
使用React Server Components的主要优势
1. 减少JavaScript下载量
服务器组件的代码不会发送到浏览器,只发送渲染结果,大幅减少用户需要下载的JS量。
例如:
使用大型日期处理库的组件,在服务器渲染后,浏览器不需要下载该库
2. 直接访问服务器资源
可以直接在组件中访问数据库或文件系统,无需额外API。
// 服务器组件可直接查询数据库
async function ProductList() {
// 直接从数据库获取数据
const products = await db.query('SELECT * FROM products');
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}3. 更快的页面加载
减少JS下载量并消除API请求,让页面加载和交互速度更快。
4. 保留客户端状态
更新服务器组件时,不会重置客户端组件的状态,使体验更流畅。
Server Components vs Client Components
| 特性 | Server Components | Client Components |
|---|---|---|
| 渲染位置 | 服务器 | 客户端浏览器 |
| 能否使用useState/useEffect | ❌ 不能 | ✅ 能 |
| 能否访问服务器资源 | ✅ 能 | ❌ 不能 |
| 事件处理 | ❌ 不支持 | ✅ 支持 |
| 对包大小的影响 | 不增加客户端包大小 | 增加客户端包大小 |
| 文件命名(Next.js) | 预设格式 | "use client" 指令 |
同时使用 Server Components 和 Client Components (Next.js 13+)
「叶子」模式
将交互组件(Client Components)尽可能推到叶子位置,让外层容器保持为Server Components。
// ❌ 不佳的做法
"use client"; // 整个页面都是Client Component
export default function Page() {
return (
<Layout>
<Header />
<Main />
<Footer />
</Layout>
);
}
// ✅ 优化的做法
// page.js (默认Server Component)
export default function Page() {
return (
<Layout>
<Header />
<Main>
<InteractiveWidget /> {/* Client Component只在需要的地方 */}
</Main>
<Footer />
</Layout>
);
}数据获取与渲染分离
在Server Components中获取数据,然后将数据作为props传递给Client Components。
// Server Component负责数据获取
async function ProductPage({ productId }) {
// 在服务器上获取数据
const product = await fetchProduct(productId);
const inventory = await fetchInventory(productId);
// 将数据传递给客户端组件进行交互
return (
<div>
<ProductInfo product={product} />
<AddToCartButton
productId={productId}
inStock={inventory.inStock}
/>
</div>
);
}
// Client Component处理交互
"use client";
function AddToCartButton({ productId, inStock }) {
// 客户端状态和事件处理
const [isAdding, setIsAdding] = useState(false);
if (!inStock) {
return <button disabled>缺货中</button>;
}
return (
<button
onClick={async () => {
setIsAdding(true);
await addToCart(productId);
setIsAdding(false);
}}
>
{isAdding ? '添加中...' : '加入购物车'}
</button>
);
}深入理解 React 水合 (Hydration) 过程
水合是React将静态HTML「注入生命」的关键过程,将静态页面转换为动态、可交互的应用程序。
🔄 完整的水合过程:
1. 服务器端渲染
产生初始HTML和数据
2. HTML传送到浏览器
用户看到静态内容
3. JS加载与解析
React代码开始执行
4. 事件监听器附加
绑定所有互动功能
5. 状态初始化
设置初始React状态
6. 完全互动状态
应用程序完全可用
⚠️ 水合过程中的常见问题
1. 内容不匹配问题
// 🚫 问题示例:服务器和客户端渲染不一致
function UserGreeting() {
// 在服务器和客户端产生不同的问候语
const greeting = new Date().getHours() < 12 ? '早安' : '午安';
return <h1>{greeting}</h1>;
}
// ✅ 解决方案:确保一致性
function UserGreeting() {
const [greeting, setGreeting] = useState('您好'); // 预设值
useEffect(() => {
// 只在客户端更新问候语
const hour = new Date().getHours();
setGreeting(hour < 12 ? '早安' : '午安');
}, []);
return <h1>{greeting}</h1>;
}2. 水合期间的效能问题
- 大量JavaScript代码导致水合延迟
- 过早执行复杂计算影响互动性
- 未正确处理异步数据加载
🔥 常见面试题目
(一) React Server Components是什么?它与传统SSR有什么不同?
解答: React Server Components (RSC) 让部分组件只在服务器上运行,减轻浏览器负担。
传统SSR
1. 整个应用在服务器渲染
2. 所有JS都发送到浏览器
3. 浏览器需水合整个应用
4. 后续更新全在浏览器端
Server Components
1. 可选择哪些组件在服务器渲染
2. 只发送Client Components的JS
3. 浏览器只水合Client Components
4. 可持续从服务器获取更新
想象SSR像是拍照片发给朋友后,朋友还需下载修图软件;而RSC是只发送修好的照片,你朋友可以直接发上IG。
(二) 何时应该使用Server Components和Client Components?
Server Components 适用于:
- 纯展示内容,无交互需求、需要直接访问数据库
- 使用大型依赖库(如日期、格式化)
- SEO重要的内容
// 直接查询数据库
async function Posts() {
const posts = await db.getPosts();
return <PostList posts={posts} />;
}Client Components 适用于:
- 需要用户交互的界面
"use client"; function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; } - 使用React Hooks (useState等)
- 需要浏览器API (localStorage等)
- 需要即时反馈的表单