ReactServer ComponentsNext.js效能優化SSR2026

React Server Components 終極指南:為什麼它會改變前端的未來

深入解析 React Server Components 的運作原理、與傳統 Server-Side Rendering 的差異、實際效能影響,以及開發者最常踩的坑。包含從 Next.js App Router 切入的實作範例。

· 6 分鐘閱讀

React Server Components(RSC)在 2024 年隨 Next.js App Router 正式進入 production,經過一年的社群驗證,已經從「實驗性功能」變成「大型應用的實際選擇」。這篇深入解析它的運作原理、為什麼效能提升如此顯著、以及開發者最常踩的五個坑。


先搞清楚:Server Components 不是 SSR

很多人聽到「Server」就以為是傳統的 Server-Side Rendering(SSR),但 Server Components 和 SSR 是兩個完全不同的概念。

傳統 SSR:字面意義的伺服器 render

請求 → 伺服器 render HTML 字串 → 傳送 HTML → 瀏覽器下載 JS → 水合(Hydration)→ 頁面可用

每次請求,伺服器都會執行 React 元件的 render function,產生 HTML 字串。瀏覽器收到 HTML 後,還需要下載完整的 JS bundle 並「水合」(Hydration)——React 在瀏覽器裡重新執行一次,綁定事件監聽,讓頁面真正可以互動。

這個流程的問題:水合昂貴,且每次請求都要重複

Server Components:只在伺服器執行的元件

請求 → 伺服器執行 Server Components → 輸出 React 元件 tree(序列化)→ 只傳送 Client Components 的 JS

Server Components 的輸出不是 HTML 字串,而是 React 的元件 tree 描述(React Server Component Payload)。這個 payload 非常小——因為 Server Components 的程式碼從來不需要送到瀏覽器

Client Components(需要互動的元件)仍然會下水合,但數量可以大幅減少。


Server Components 的核心價值:零 bundle 成本

讓我們用數字說話。以下是一個典型電子商務產品頁面的情境:

假設場景: 一個有 200 個 React 元件的頁面,其中 180 個是「展示型」元件(只 render,不互動),20 個是「互動型」元件(有事件處理)。

方案Client JS bundle水合成本
傳統 SPA全部 200 個元件全部 200 個元件
SSR全部 200 個元件全部 200 個元件
Server Components僅 20 個 Client 元件僅 20 個 Client 元件

在 Next.js App Router 環境下,180 個展示型元件的程式碼完全不會進入 client bundle,等於減少了 90% 的水合成本。

這就是為什麼 Vercel 展示的 benchmark 中,複雜頁面的 Time to Interactive(TTI)可以降低 30-50%。


實作:Next.js App Router 的 Server Components

以下用 Next.js App Router 說明實際的寫法。

預設是 Server Component

在 App Router 中,所有元件預設都是 Server Components

// app/products/page.tsx
// 這個檔案是 Server Component——預設!
// 可以直接使用:async/await、直接查詢資料庫、讀取檔案

import { db } from '@/lib/db';

export default async function ProductsPage() {
  // 直接在這裡用 async/await,不需要 useEffect 或 API Route
  const products = await db.product.findMany({
    orderBy: { createdAt: 'desc' },
    take: 20,
  });

  return (
    <main>
      <h1>最新商品</h1>
      <div className="product-grid">
        {products.map((product) => (
          // ProductCard 也是 Server Component——不需要 "use client"
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </main>
  );
}

何時需要 Client Component:use client

當元件需要互動(有事件處理、useState、useEffect)時,必須明確標記為 Client Component:

// components/AddToCartButton.tsx
'use client'; // ← 明確標記這是 Client Component

import { useState } from 'react';

export function AddToCartButton({ productId }) {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(c => c + 1)}>
      加入購物車 {count > 0 && `(${count})`}
    </button>
  );
}
// components/ProductCard.tsx
// 這個是 Server Component——沒有 'use client'
import { AddToCartButton } from './AddToCartButton'; // 引入 Client Component

export function ProductCard({ product }) {
  // 這裡不能使用任何 hooks 或事件處理
  // 但可以 render Client Component

  return (
    <div className="product-card">
      <img src={product.image} alt={product.name} />
      <h2>{product.name}</h2>
      <p>NT$ {product.price}</p>

      {/* Client Component:可以正常運作 */}
      <AddToCartButton productId={product.id} />
    </div>
  );
}

Server Component 巢狀 Client Component:組合是關鍵

Server Component 可以 render Client Component,但 Client Component 裡不能 import Server Component(因為 Client Component 最終會下水合到瀏覽器,而 Server Component 的程式碼不會在瀏覽器執行)。

// ✅ 合法:Server Component render Client Component
// page.tsx(Server Component)
import { ClientCounter } from './ClientCounter'; // OK

export default async function Page() {
  const data = await fetchData(); // OK: Server-only
  return <ClientCounter initialCount={data.count} />; // OK
}

// ❌ 非法:Client Component import Server Component
// ClientComponent.tsx
'use client';
import { ServerComponent } from './ServerComponent'; // ❌ 編譯錯誤

export function ClientComponent() {
  // ...
}

實際資料存取:直接查資料庫

Server Components 最大的實用價值:可以直接在元件裡查資料庫,不需要 API Route:

// app/dashboard/page.tsx
import { db } from '@/lib/db';
import { currentUser } from '@/lib/auth';

export default async function DashboardPage() {
  const user = await currentUser();

  // 兩個查詢同時發起(真正的並行)
  const [recentOrders, recommendations] = await Promise.all([
    db.order.findMany({
      where: { userId: user.id },
      orderBy: { createdAt: 'desc' },
      take: 5,
    }),
    db.product.findMany({
      where: { category: user.preferredCategory },
      take: 6,
    }),
  ]);

  return (
    <DashboardLayout>
      <RecentOrders orders={recentOrders} />
      <Recommendations products={recommendations} />
    </DashboardLayout>
  );
}

過去這需要:

  1. 建立 API Route /api/dashboard
  2. Client Component 裡用 useEffect + fetch('/api/dashboard')
  3. 管理 loading 狀態

現在只需要一個 async Server Component。


常見的五個誤區

誤區一:Server Components 可以用 hooks

不行。Hooks 是 React 的 client-side 機制,依賴 component 的 render phase。Server Components 的執行階段(render-on-server)和 Client Components 的執行階段完全不同。

// ❌ Server Component 裡不能這樣用
export default async function Page() {
  const [data, setData] = useState(null); // ❌ Error: useState is not defined
  // ...
}

誤區二:把整個頁面標記為 ‘use client’ 就可以用 Server Components 的好處

錯。把頁面標記為 use client 代表整個 subtree 都是 Client Components,等於放棄了 Server Components 的 bundle 優勢。

原則:只在必要的地方使用 use client,其餘保持 Server Component。

誤區三:Server Components 適合所有類型的資料

不對。Server Components 適合靜態或半靜態的資料(產品目錄、使用者資料、部落格文章)。對於需要即時、頻繁更新的資料(如股價、直播評論),仍然需要 Client Components + WebSocket / polling。

誤區四:RSC 和 SSR 一樣,不利於 SEO

相反。Server Components 的 HTML 输出和 SSR 一樣對 SEO 友好,同時因為首屏 JS 減少,爬蟲可以更早互動頁面。事實上,Google 在 2024 年的官方文件中,已經把使用 RSC 的頁面列為「頁面體驗訊號高分」的重要手段之一。

誤區五:Server Components 讓 API Routes 過時

不完全是。API Routes(/app/api/...)在以下情境仍然必要:

  • 作為外部服務的 Webhook 端點
  • 需要從 client 端上傳檔案
  • 跨域 API 代理
  • Server Components 無法直接處理的非同步操作(如檔案上傳後的處理)

Server Components 與 Client Components 的決策框架

一個 Server/Client 的 decision tree:

這個元件需要...
  ├── 使用 hooks(useState, useEffect, useRef, useContext)?
  │   └── 是 → Client Component ('use client')
  ├── 掛載事件監聽(onClick, onChange, onSubmit)?
  │   └── 是 → Client Component
  ├── 需要存取瀏覽器 API(window, localStorage, navigator)?
  │   └── 是 → Client Component
  └── 以上都不是?
      └── Server Component(預設)

Server Components 的效能實測數據

以下是 Next.js 官方和社群在 2025 年下半年發布的 benchmark 數據:

指標傳統 SSRRSC
Client JS bundle100%30-70%(視頁面複雜度)
Time to First Byte(TTFB)相近相近或略好
Time to Interactive(TTI)基準-30% ~ -50%
Largest Contentful Paint(LCP)相近-20% ~ -40%
水合時間全頁水合僅 Client Components

這些數據在不同的應用場景差異很大——頁面越複雜、展示型元件越多,RSC 的優勢越明顯。


總結:什麼時候選 Server Components

適合使用 Server Components:

  • 大型內容驅動型應用(電商、部落格、文件網站)
  • 有大量展示型元件的頁面
  • 需要良好 SEO 的頁面
  • 需要直接存取資料庫或檔案系統的場景

不適合(過渡到 Client Components):

  • 即時互動介面(聊天、遊戲、協作工具)
  • 高度依賴 client-side 狀態的複雜表單
  • 需要頻繁更新 UI 的資料視覺化元件
  • 大量使用第三方 client-side 函式庫

Server Components 不是 SSR 的替代品,而是一個互補的範式。最健康的架構是:Server Components 處理資料和展示,Client Components 處理互動,兩者根據上面的 decision tree 明確分工。


延伸閱讀


本文基於 React Server Components 官方文件與 Next.js App Router 2026 年初的最佳實踐整理。