文章中英模式
布魯斯前端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等)
- 需要即時反饋的表單