Next.js Server Components 與 Client Components
Next.js 的 App Router 建立在 React 的最新特性上:伺服器元件 (Server Components) 與 客戶端元件 (Client Components)。理解這兩者的區別與合作方式,是開發高效能應用的不二法門。
伺服器元件 (Server Components)
在 Next.js 中,app 目錄下的所有元件預設都是伺服器元件。它們直接在伺服器端執行並渲染成 HTML,然後發送到瀏覽器。
優點:
- 減少 JS 體積:伺服器端的套件(如
fs,sql)不會被下載到瀏覽器,這讓用戶端載入變極快。 - SEO 友善:搜尋引擎爬蟲可以直接讀取完整的 HTML。
- 安全性:敏感資訊(如 API Keys)保存在伺服器端,不會外流。
- 直接讀取資料庫:你可以直接在元件內撰寫
async/await來抓取數據。
// 預設就是 Server Component
async function getPosts() {
const res = await fetch('https://api.example.com/posts');
return res.json();
}
export default async function BlogPage() {
const posts = await getPosts(); // 在伺服器端執行
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
客戶端元件 (Client Components)
如果你需要與使用者進行互動(如點擊事件、計時器、使用 React Hook 等),你就必須將元件標註為客戶端元件。
如何宣告?
在檔案的最頂端加上 'use client'; 指令。
'use client'; // 標註為 Client Component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>點擊次數:{count}</button>;
}
何時使用哪種組件?
| 需求 | Server Component | Client Component |
|---|---|---|
| 擷取數據 (Fetch Data) | ✅ (推薦) | ❌ |
| 存取後端資源 (直接讀 DB) | ✅ | ❌ |
點擊事件、狀態操作 (onClick, useState) | ❌ | ✅ |
使用瀏覽器 API (window, localStorage) | ❌ | ✅ |
| 減少用戶端 JavaScript | ✅ | ❌ |
組合模式:最佳實踐
- 下移 Client Components:盡量保持 Server Components 的層級,只在需要互動的小區塊使用
'use client'。 - 傳入 Props 而非 Import:如果你需要在 Client Component 裡面包含一個 Server Component,請透過
children傳入。
// ❌ 錯誤:在 Client Component 中 import Server Component 會使其變成 Client 組件
// ✅ 正確:
export default function ParentServerComponent() {
return (
<MyClientLayout>
<ServerSidePosts /> {/* 作為 children 傳入 */}
</MyClientLayout>
);
}
在 React 的架構中,Client Component 位於渲染樹(Render Tree)的葉子節點或較低層級。當你直接 import 一個 Server Component 到 Client Component 時,React 為了確保瀏覽器能夠執行這些程式碼,會將該元件及其所有依賴項一起打包(Bundle)到客戶端。
這時,該元件會失去 Server Component 的特權(例如:直接存取資料庫、使用伺服器端環境變數等),並變成一個普通的 Client Component 在瀏覽器執行。
小結
Next.js 讓你在同一個專案中混合這兩種模式。口訣是:預設使用 Server Components,直到你需要使用者互動時才下移至 Client Components。