Astro
Astro 負責靜態結構:頁面、佈局以及純展示區域。
SolidJS 或 React 負責所有互動性:每個處理使用者輸入、管理狀態或回應事件的元件,都必須是一個框架島嶼(framework island),而非 .astro 檔案。
所有元件樣式皆使用 CSS Modules,且一律放在獨立的 styles.module.css 檔案中,絕不內嵌於 Astro <style> 區塊內。
何時使用此技能
- 規劃新的 Astro 專案結構,或新增頁面與佈局區域
- 建立
.astro頁面、佈局或靜態展示區域 - 判斷某個項目應放在 Astro 還是 SolidJS/React 島嶼中
- 為島嶼選擇合適的 hydration 指令
- 在
.astro檔案或框架島嶼中應用 CSS Modules 樣式 - 處理內容集合(
src/content/、src/content.config.ts) - 將服務層資料傳入伺服器渲染的 Astro 頁面
- 使用伺服器島嶼(
server:defer)實現個人化的延遲渲染區域 - 透過適配器與中介軟體設定隨需渲染(SSR)
- 定義並呼叫 Astro Actions 以進行型別安全的伺服器端變更
Astro 與島嶼的核心架構決策
這是 Astro 專案中最重要的決策。
| 用途 | 使用哪種技術 |
|---|---|
| 從資料渲染靜態 HTML — 無使用者互動 | .astro |
| 頁面外殼、佈局容器、導覽結構 | .astro |
| 內容區塊、部落格文章、行銷區域 | .astro |
任何包含 onClick、onChange、表單狀態或反應式資料的元件 | SolidJS 或 React 島嶼 |
| 基礎 UI 元件(Button、Input、Modal、Alert 等) | SolidJS 或 React 島嶼 |
| 任何讀取或寫入 store 或服務 hook 的元件 | SolidJS 或 React 島嶼 |
簡而言之的規則:
- Astro 渲染結構與內容,但不處理使用者互動。
- SolidJS 或 React 負責所有互動、所有表單狀態以及所有基礎互動式 UI 原語 — 即使只是觸發事件的簡單按鈕。
- 不要在
.astro檔案中建立互動式 UI。Astro 不具有反應性,嘗試在.astro檔案中加入互動性,會產生脆弱且難以測試的程式碼。
該選哪個框架 — SolidJS 還是 React?
專案應一致使用其中一個。請檢查 astro.config.mjs 中的設定整合(@astrojs/solid-js 或 @astrojs/react),並在整個專案中對所有島嶼使用該框架。請勿在同一個專案中混用兩個框架。
詳情請參閱 ./references/islands-and-hydration.md 以獲得完整的決策指南與 hydration 指令參考。
樣式規則 — 使用分離的 CSS Modules 檔案
這是此技能中最關鍵的規則。
每個 Astro 元件、頁面與佈局都使用 CSS Modules 進行範圍樣式設定。CSS 存在於獨立的檔案中 — 絕對不使用 Astro 的 <style> 標籤。
ComponentName/
├── index.astro ← 元件邏輯與標記
└── styles.module.css ← 此元件的所有樣式,一律使用獨立檔案
- 永遠在
index.astro旁建立styles.module.css。 - 絕對不要將樣式放在 Astro 的
<style>區塊內。 - 絕對不要使用內聯
style屬性。 - 在 frontmatter 區塊頂端匯入模組:
import styles from './styles.module.css' - 使用
class:list指令或直接類別繫結來套用類別:<div class={styles.container}><div class:list={[styles.container, isActive && styles.active]}>
- 使用語意化的類別名稱,不要堆疊工具類別。
- 使用 CSS 自訂屬性(custom properties)來定義所有設計符記(顏色、間距、排版)。
- 不使用
!important。不使用深度選擇器。不使用 ID 選擇器設定樣式。
詳情請參閱 ./references/styling-and-css-modules.md 以獲得符記策略、主題變數、響應式規則與動畫指引。
請參閱 ./examples/button-component/ 以獲得完整的 Button 範例與獨立的 CSS Module 檔案。
專案結構
src/
├── components/
│ ├── foundation/ # 互動式 UI 原語 — SolidJS 或 React,不是 .astro
│ │ ├── Button/
│ │ │ ├── index.tsx ← SolidJS (.tsx) 或 React (.tsx)
│ │ │ └── styles.module.css
│ │ └── Input/
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── layout/ # 靜態佈局結構 — .astro
│ │ ├── Header/
│ │ │ ├── index.astro
│ │ │ └── styles.module.css
│ │ └── Footer/
│ │ ├── index.astro
│ │ └── styles.module.css
│ └── features/ # 互動式功能區域 — SolidJS 或 React
│ └── SomeFeature/
│ ├── index.tsx ← 島嶼元件
│ └── styles.module.css
├── layouts/
│ └── BaseLayout/
│ ├── index.astro ← 佈局外殼永遠是 .astro
│ └── styles.module.css
├── pages/
│ ├── index/
│ │ ├── index.astro ← 頁面永遠是 .astro
│ │ └── styles.module.css
│ └── [dynamic]/
│ ├── index.astro
│ └── styles.module.css
├── content/
│ ├── blog/
│ └── [collection-name]/
├── content.config.ts ← Astro 5 集合綱要與載入器
├── store/
│ ├── repository/
│ ├── service/
│ └── config.ts
├── styles/
│ ├── tokens/
│ │ ├── _colors.css
│ │ ├── _spacing.css
│ │ └── _typography.css
│ ├── global.css
│ └── themes/
│ ├── light.css
│ └── dark.css
└── utils/
命名慣例:
.astro頁面與佈局:PascalCase/資料夾,內含index.astro- 框架島嶼元件:
PascalCase/資料夾,內含index.tsx - 頁面:
kebab-case/資料夾,內含index.astro或[dynamic]/內含index.astro - CSS Module 檔案:一律命名為
styles.module.css— 每個元件目錄一個 - 工具函式:
kebab-case.ts
詳情請參閱 ./references/project-structure.md 以獲得目錄職責與命名慣例的完整說明。
Astro 元件樣式
.astro 檔案僅用於靜態結構。它們從 props 與資料渲染 HTML — 不處理事件或管理狀態。
- 在 frontmatter 區塊中使用
interface Props定義型別化 props。 - 透過
Astro.props存取 props。 - 保持元件小型且單一職責。
- 偏好組合而非 prop drilling。
- 保持表達式簡單;將複雜邏輯移至 frontmatter 變數。
- 使用語意化的 HTML 元素 —
<section>、<article>、<nav>、<main>、<header>— 而不是通用的<div>容器(當元素具有語意意義時)。
---
import styles from './styles.module.css'
interface Props {
title: string
subtitle?: string
}
const { title, subtitle } = Astro.props
---
<section class={styles.hero} aria-label={title}>
<h1 class={styles.title}>{title}</h1>
{subtitle && <p class={styles.subtitle}>{subtitle}</p>}
<slot />
</section>
詳情請參閱 ./references/component-patterns.md 以獲得 frontmatter 慣例、slot 用法與 Astro API 參考。
基礎元件 — SolidJS 或 React,不是 Astro
基礎 UI 原語(Button、Input、Alert、Modal、Select、Toggle 等)永遠以 SolidJS 或 React 元件建構。它們絕對不是 .astro 檔案。
原因: 基礎元件處理使用者事件、管理焦點、暴露 onClick 與 onChange 回呼,有時還包含內部狀態。這些在 .astro 檔案中都無法做到。將「按鈕」建構為 .astro 會產生無法以型別安全、可重複使用方式回應點擊的靜態 HTML。
決策規則:
- 原語已存在 — 直接使用。
- 原語接近需求但缺少某個 prop — 擴展現有的原語。
- 原語不存在 — 在
components/foundation/中以 SolidJS 或 React 元件建立它之後再使用。絕不要在頁面或功能元件中內嵌一次性互動元素。
所有基礎元件必須一致、可存取且強型別。請參考 solid.js 或 react 技能以了解島嶼元件內適用的實作慣例。
島嶼與 Hydration
每個互動式元件都是一個島嶼。從 .astro 頁面與佈局中使用 client:* 指令掛載島嶼。選擇能滿足互動需求的最小指令。
| 指令 | 使用時機 |
|---|---|
client:load | 必須在頁面載入時立即互動 |
client:idle | 可以等到瀏覽器空閒時再處理 |
client:visible | 僅在其進入視口時才需要 hydrate |
client:media="(query)" | 僅在特定視口尺寸下互動 |
client:only="solid-js" 或 client:only="react" | 需要瀏覽器 API 才能渲染;跳過 SSR |
預設使用 client:visible 或 client:idle。僅在使用者會立即互動的 UI(主要表單、導覽下拉選單、驗證頁面)上使用 client:load。
島嶼元件始終擁有自己的 styles.module.css:
components/features/ContactForm/
├── index.tsx ← SolidJS 或 React 島嶼
└── styles.module.css ← 獨立的 CSS Module,與其他規則相同
從 Astro 頁面掛載島嶼:
---
import styles from './styles.module.css'
import ContactForm from '../../components/features/ContactForm/index.tsx'
const { prefillEmail } = Astro.props
---
<section class={styles.section}>
<h2 class={styles.heading}>聯絡我們</h2>
<ContactForm client:visible prefillEmail={prefillEmail} />
</section>
詳情請參閱 ./references/islands-and-hydration.md 以獲得完整的決策指南、框架特定注意事項與資料傳遞規則。
內容集合
內容集合是管理 Markdown、MDX 與結構化資料檔案的標準方式,並提供完整的 TypeScript 型別安全性。
Astro 5 主要變更:
- 設定檔位於
src/content.config.ts(非src/content/config.ts) - 集合使用
loader— 從astro/loaders匯入的glob()或file() render(entry)是從astro:content匯入的函式(非entry.render())- 從
astro:content匯入z,而非直接從zod匯入
// src/content.config.ts
import { defineCollection, z, reference } from 'astro:content'
import { glob } from 'astro/loaders'
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
})
export const collections = { blog }
在頁面中渲染:
---
import { getEntry, render } from 'astro:content'
const post = await getEntry('blog', Astro.params.slug)
const { Content } = await render(post)
---
<Content />
詳情請參閱 ./references/content-collections.md 以獲得完整 API:載入器、跨集合參照、自訂載入器、執行時期集合與動態路由模式。
伺服器島嶼、SSR 與 Actions
伺服器島嶼 (server:defer)
將個人化或動態的 .astro 元件延遲處理,同時立即提供頁面外殼。需要適配器。
<UserCart server:defer>
<span slot="fallback">···</span>
</UserCart>
延遲的元件在伺服器端執行並串流輸出。用於原本會阻塞靜態頁面傳遞的使用者特定 UI(購物車數量、通知、個人化問候語)。
隨需渲染 (SSR)
透過 export const prerender = false(混合模式)將頁面加入伺服器渲染,或全域設定 output: 'server'。
SSR 頁面可以存取 Astro.cookies、Astro.request、Astro.redirect() 與 Astro.locals(由中介軟體填入)。
中介軟體
在 src/middleware.ts 中定義請求攔截邏輯:
import { defineMiddleware } from 'astro:middleware'
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.user = await getUser(context.cookies.get('session')?.value)
return next()
})
Astro Actions
在 src/actions/index.ts 中定義型別安全的伺服器端函式,可從島嶼或 HTML 表單呼叫:
// src/actions/index.ts
import { defineAction, ActionError } from 'astro:actions'
import { z } from 'astro:content'
export const server = {
newsletter: {
subscribe: defineAction({
input: z.object({ email: z.string().email() }),
handler: async ({ email }) => {
await addSubscriber(email)
return { success: true }
},
}),
},
}
從島嶼中呼叫:
import { actions } from 'astro:actions'
const { data, error } = await actions.newsletter.subscribe({ email })
使用 isInputError(error) 來區分 Zod 驗證錯誤與處理器錯誤。
詳情請參閱 ./references/server-rendering.md 以獲得完整內容:渲染模式、適配器設定、中介軟體、伺服器島嶼、隨需 API、表單動作、錯誤碼、型別化環境變數 (astro:env) 與視圖轉場。
Astro API 參考
Astro.props— 型別化的元件 propsAstro.params— 動態路由參數 ([slug]、[...path])Astro.request— 伺服器端請求物件(標頭、cookie、URL)Astro.url— 目前頁面 URLgetStaticPaths()— 列舉靜態網站生成的路徑getEntry()/getCollection()— 內容集合查詢
服務層資料流程
Astro 頁面與佈局是在 frontmatter 區塊中呼叫服務或儲存庫函式的正確位置。元件應以 props 形式接收資料。
- 在頁面 frontmatter 中取得資料,而非在元件內部。
- 將資料以型別化 props 向下傳遞。
- 元件不得直接呼叫儲存庫或外部 API。
- 將驗證與業務邏輯保留在服務層。
可存取性
- 當元素具有意義時,使用語意化的 HTML 元素。
- 在真正提供額外語意資訊的地方加入 ARIA 屬性。
- 確保所有互動式元素皆具有可存取的名稱。
- 維持可見的焦點狀態。
- 互動式元素的最小觸控目標大小為 44px。
- 尊重
prefers-reduced-motion。 - 在頁面內容中按邏輯順序安排標題層級(
h1→h2→h3)。
詳情請參閱 ./references/accessibility-patterns.md 以獲得 ARIA 模式、表單標籤與標題階層指引。
支援檔案
-
./references/styling-and-css-modules.mdCSS Modules 規則、符記策略、主題變數、響應式與動畫。 -
./references/islands-and-hydration.md決定是否為島嶼、選擇哪個框架、hydration 指令選擇指南、資料傳遞規則、框架特定注意事項。 -
./references/component-patterns.mdAstro frontmatter 慣例、slot 用法、元件組合模式、Astro物件 API。 -
./references/content-collections.md定義集合(Astro 5 語法)、查詢與渲染條目、跨集合參照、自訂載入器。 -
./references/server-rendering.md渲染模式(靜態、混合、伺服器)、適配器設定、中介軟體、伺服器島嶼、Astro Actions、表單動作、錯誤代碼、astro:env、檢視轉場。 -
./references/accessibility-patterns.mdARIA 模式、表單標籤、錯誤訊息、標題階層、焦點管理、prefers-reduced-motion。 -
./examples/button-component/完整的 Button 範例,使用 SolidJS 或 React 與獨立的 CSS Module 檔案。 -
./examples/contact-form/包含表單驗證的 ContactForm 島嶼,使用 Astro Actions 進行型別安全的伺服器端處理。