Server Components 與 Client Components
App Router 裡所有元件預設都是 Server Component:只在伺服器端執行、渲染結果直接送 HTML 給瀏覽器,元件本身的 JavaScript 程式碼不會下載到客戶端。這個預設值是 Next.js 效能好的關鍵原因,也是跟傳統 React 專案最大的思維轉換。
為什麼要分兩種
- Server Component 可以直接讀資料庫、呼叫私密 API、使用環境變數裡的密鑰,因為程式碼從頭到尾只在伺服器跑,不會被打包進瀏覽器、不會外洩。同時不佔用瀏覽器的 JavaScript 執行量,首屏載入更快。
- Client Component 需要瀏覽器才能做的事:
useState、useEffect、事件監聽(onClick)、瀏覽器專屬 API(如localStorage)。這種元件的程式碼會被打包送到瀏覽器執行。
何時要加 "use client"
在檔案最上方加上 "use client",這個檔案(以及它 import 的其他元件)就會被視為 Client Component:
components/LikeButton.tsx
"use client";
import { useState } from "react";
export default function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "❤️ 已收藏" : "🤍 收藏"}
</button>
);
}
判斷原則很單純:沒有用到互動或瀏覽器 API 就不要加,讓元件盡量留在伺服器端執行。
組合模式
實務上一個頁面通常是 Server Component 為主、少數需要互動的地方才用 Client Component 包起來,兩者可以自由組合:
app/posts/[id]/page.tsx
import LikeButton from "@/components/LikeButton";
export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const post = await getPost(id); // 直接在伺服器端抓資料
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
<LikeButton /> {/* 只有這裡需要互動,其餘維持 Server Component */}
</article>
);
}
有一個容易搞混的限制:Client Component 不能直接 import Server Component(因為 Client Component 的程式碼要送到瀏覽器,而 Server Component 可能含有只能在伺服器跑的程式碼)。如果要在 Client Component 裡放 Server Component 的內容,做法是把 Server Component 當成 children 傳進去:
// Server Component 把另一個 Server Component 當 children 傳給 Client Component 是允許的
<ClientWrapper>
<ServerComponent />
</ClientWrapper>
推薦影音
Server 與 Client Components 實例解說
簡述:Codevolution 這支影片用具體範例示範兩種元件的差異、"use client" 的作用範圍,以及常見的組合寫法,適合搭配上面的說明對照練習。
下一步
了解元件分工後,接著看 Server Component 如何實際抓取資料,詳見 資料抓取。