Astro
Astro 负责静态结构:页面、布局和纯展示区域。
SolidJS 或 React 负责所有交互性:每个处理用户输入、管理状态或响应事件的组件都是框架孤岛——而不是 .astro 文件。
所有组件样式均使用 CSS Modules——始终放在独立的 styles.module.css 文件中,从不内联或放在 Astro 的 <style> 块内。
何时使用此技能
- 构建新的 Astro 项目或添加页面与布局区域
- 构建
.astro页面、布局或静态展示区域 - 决定将某部分放在 Astro 中还是 SolidJS/React 孤岛中
- 为孤岛选择合适的水合指令
- 在
.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 或 service 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 获取完整的决策指南和水合指令参考。
样式规则——CSS Modules 在独立文件中
这是本技能中最关键的规则。
每个 Astro 组件、页面和布局都使用 CSS Modules 实现作用域样式。CSS 应放在独立文件中——绝不要放在 Astro 的 <style> 标签内。
ComponentName/
├── index.astro ← 组件逻辑和标记
└── styles.module.css ← 该组件的所有样式,始终是独立文件
- 始终 在
index.astro旁边创建styles.module.css。 - 绝不 将样式放入 Astro 的
<style>块中。 - 绝不 使用内联
style属性。 - 在 frontmatter fence 顶部导入模块:
import styles from './styles.module.css' - 使用
class:list指令或直接的 class 绑定来应用类:<div class={styles.container}><div class:list={[styles.container, isActive && styles.active]}>
- 使用语义化的类名,不要堆叠工具类。
- 使用 CSS 自定义属性管理所有设计令牌(颜色、间距、排版)。
- 不使用
!important。不使用深层选择器。不使用 ID 选择器进行样式化。
阅读 ./references/styling-and-css-modules.md 获取令牌策略、主题变量、响应式规则和动画指南。
阅读 ./examples/button-component/ 获取包含独立 CSS Module 文件的完整 Button 示例。
项目结构
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 集合 schemas 和 loaders
├── 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 fence 中使用
interface Props定义类型化 props。 - 使用
Astro.props访问 props。 - 保持组件小巧且职责单一。
- 优先使用组合而非 props 透传。
- 保持表达式简单;将复杂逻辑移动到 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 技能了解孤岛组件内的实现约定。
孤岛与水合
每个交互式组件都是一个孤岛。从 .astro 页面和布局中使用 client:* 指令挂载孤岛。选择满足交互需求的最小指令。
| 指令 | 使用时机 |
|---|---|
client:load | 页面加载后必须立即可交互 |
client:idle | 可以等到浏览器空闲时再交互 |
client:visible | 仅当进入视口时才需要水合 |
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:loaders、跨集合引用、自定义 loaders、运行时集合和动态路由模式。
服务端孤岛、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、带表单的 actions、错误码、类型化环境变量(astro:env)和视图过渡。
Astro API 参考
Astro.props——类型化组件 propsAstro.params——动态路由参数([slug],[...path])Astro.request——服务端请求对象(headers, cookies, URL)Astro.url——当前页面 URLgetStaticPaths()——枚举静态站点生成的路径getEntry()/getCollection()——内容集合查询
服务层数据流
Astro 页面和布局是在 frontmatter fence 中调用服务或仓库函数的正确位置。组件应通过 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完整的孤岛与水合决策指南、框架特定说明和数据传递规则。./references/component-patterns.mdFrontmatter 约定、slot 用法和 Astro API 参考。./references/content-collections.md内容集合 API 参考:loaders、跨集合引用、自定义 loaders 和动态路由模式。./references/server-rendering.md渲染模式、适配器配置、中间件、服务端孤岛、按需 API、actions、错误码、类型化环境变量和视图过渡。./references/project-structure.md目录职责和命名约定。./references/accessibility-patterns.mdARIA 模式、表单标签和标题层级指南。./examples/button-component/包含独立 CSS Module 文件的完整按钮示例。