第四課:Server Islands 與 SSR (伺服器端渲染)
在之前的課程中,我們的網站是「靜態」的。
但在真實應用中,有些內容不能是靜態的(例如:用戶登入狀態、即時庫存、或是根據地理位置推薦的內容)。
1. 核心觀念:Server Islands vs. Client Islands
- Client Islands (
client:load): 在瀏覽器下載 JS 並執行。適合互動(按鈕、滑動視窗)。 - Server Islands (
server:defer): 這是 Astro 5 的新武器。它允許頁面大部分是靜態的(極速),但將「慢速或動態」的部分留給伺服器處理,並在處理完後自動補洞。
2. 手把手實作:開啟 SSR 模式
要使用伺服器功能,我們需要把輸出模式改為 server。
- 在終端機執行(以 Vercel 為例,你也可以選 Node.js):
npx astro add vercel - 這會自動修改你的
astro.config.mjs,將output設為'server'。
當你添加了adapter: vercel()卻沒有看到output屬性時,這代表 Astro 目前正處於 「混合模式 (Hybrid Mode)」或「伺服器端渲染模式 (SSR)」 的預備狀態,但預設值可能尚未顯式寫出,需自行加入。// @ts-check
import { defineConfig } from 'astro/config'
import react from '@astrojs/react'
import vercel from '@astrojs/vercel'
// https://astro.build/config
export default defineConfig({
integrations: [react()],
adapter: vercel(),
output: 'server',
})
3. 手把手實作:建立一個 Server Island
假設我們要在部落格底部顯示「當前伺服器時間」,這個時間必須是準確的,不能在構建時寫死。
建立 src/components/ServerTime.astro:
---
// 這個組件會在伺服器上執行
const time = new Date().toLocaleTimeString();
// 模擬一個慢速的資料庫查詢
await new Promise(resolve => setTimeout(resolve, 2000));
---
<div class="server-box">
<p>伺服器即時時間:{time}</p>
<p>(此組件是延遲載入的 Server Island)</p>
</div>
<style>
.server-box { border: 2px dashed #ff5d01; padding: 1rem; background: #fff5f0; }
</style>
在 src/pages/index.astro 中使用它:
---
import BaseLayout from '../layouts/BaseLayout.astro';
import ServerTime from '../components/ServerTime.astro';
---
<BaseLayout pageTitle="Astro 5 實驗室">
<h2>歡迎來到我的動態首頁</h2>
<ServerTime server:defer>
<p slot="fallback" class="loading">正在連線伺服器...</p>
</ServerTime>
</BaseLayout>
📝 第四課練習題
任務目標:建立一個動態的「隨機推薦」組件。
- 建立一個名為
RandomQuote.astro的組件。 - 在該組件的 Code Fence 中,使用
fetch()從 API 抓取一句隨機名言(例如:https://api.quotable.io/random)。 - 在
about.astro頁面中,以server:defer的方式引入這個組件。 - 設定一個具備 Loading 樣式的
fallback內容。 - 進階挑戰: 嘗試將原本的部落格清單頁面
blog.astro改為混合模式——頁面頂部標題是靜態的,但文章清單改由server:defer渲染。
第四課練習題實作放在下一篇 ( 我也是第一次學習,自己試作,不一定是對的喔👍 )
然後 https://api.quotable.io/random 憑證過期了,可以找別的 api 來玩
📚 額外筆記
※ server:defer
在 Astro 裡:
<ServerTime server:defer />
這是一個 Astro 指令(directive),意思是:
👉 把這個元件延後到「伺服器端」再執行 / 回傳內容
server:defer = 不要在建置或一開始就跑,等請求進來再由 Server 動態產生
拆開來看
1️⃣ <ServerTime />
這是一個 Astro / Framework 元件
通常用來顯示「現在伺服器時間」
---
// ServerTime.astro
const now = new Date().toLocaleString()
---
<p>Server Time: {now}</p>
2️⃣ server:defer 是什麼?
它是 Astro 的 Server Directive
server:defer
代表:

實際行為差異
❌ 沒用 server:defer
<ServerTime />
👉 在 build 時就固定
👉 所有人看到一樣的時間(不會變)
✅ 使用 server:defer
<ServerTime server:defer />
👉 每次 request 才計算時間
👉 顯示真正的「現在時間」
適合用在什麼情境?
✅ 適合
- 現在時間
- 後端 API 結果
- Cookie / Header 相關資料
- 使用者相關狀態(未登入前端 JS)
❌ 不適合
- 純靜態內容
- 需要前端互動(點擊、動畫)
和其他指令的對比

※ <p slot="fallback">...</p>
👉 這是「後端內容還沒回來之前,先顯示的畫面」
你的程式碼在做什麼
<ServerTime server:defer>
<p slot="fallback" class="loading">
正在連線伺服器...
</p>
</ServerTime>
實際流程是:
1️⃣ 頁面一開始載入
Astro 不會立刻有 <ServerTime /> 的結果
因為你用了 server:defer
👉 所以先顯示:
<p class="loading">正在連線伺服器...</p>
2️⃣ 伺服器把 ServerTime 算好後
Astro 會:
- 把
<ServerTime />的 HTML 傳回來 - 自動把 fallback 換掉
結果變成:
<p>Server Time: 2026/01/04 11:23:45</p>
👉 使用者不需要重整頁面
👉 也沒有任何前端 JS hydrate
那個 <p> 到底是什麼?
🔹 它不是普通子元件
它是:
Astro 的 slot fallback
slot="fallback"
是 Astro 保留的 slot 名稱
🔹 為什麼一定要用 slot="fallback"?
因為 Astro 在 server:defer 時:
- 主內容 → 等 server 回應
- fallback slot → 立刻顯示
如果你這樣寫:
<ServerTime server:defer>
<p>正在連線伺服器...</p>
</ServerTime>
❌ 不會顯示
(因為沒指定 fallback slot)
🔹 視覺化流程
頁面載入
│
├─ 顯示 <p class="loading">正在連線伺服器...</p>
│
├─ ServerTime 在 Server 執行
│
└─ 回傳 HTML → fallback 被替換
和 client 指令的 loading 差別

實務常見用法
<UserProfile server:defer>
<div slot="fallback" class="skeleton">
使用者資料載入中…
</div>
</UserProfile>
<CartTotal server:defer>
<span slot="fallback">計算中...</span>
</CartTotal>









