关于 web-meta-framework-astro
面向 Astro 开发者的模式指南,涵盖组件、内容集合、路由与渲染方式。重点说明默认静态输出、按需交互岛屿及逐页渲染控制,并包含 Astro 6 的迁移注意事项。
主要功能
- 01Astro 组件与布局模式
- 02交互岛屿与客户端指令
- 03带 Zod 校验的内容集合
- 04静态及按需路由
- 05页面级渲染控制
- 06Astro 6 注意事项
适用场景
- 01为 Astro 页面选择静态或按需渲染
- 02为组件配置岛屿水合时机
- 03定义并查询类型化 Markdown 内容
- 04检查 Astro 6 项目的迁移问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-meta-framework-astro安装后在 Claude Code 中输入/web-meta-framework-astro确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/content.mdcore.mdintegrations.mdislands.mdrouting.md详细介绍
Astro 模式
快速指南: Astro 默认将页面渲染为静态 HTML,且不包含客户端 JavaScript。
.astro组件承载静态内容;只有添加client:*指令后,框架组件才会成为可交互的岛屿。内容集合通过 Zod 架构为结构化 Markdown frontmatter 提供类型安全保障。渲染模式按页面设置:默认静态,除非设置export const prerender = false。Astro 6 将<ViewTransitions />替换为<ClientRouter />,将Astro.glob()替换为import.meta.glob(),将z从astro:content移至astro/zod(现为 Zod 4),要求getStaticPaths()参数为字符串,并要求 Node 22.12.0+。
详细资源:
- examples/core.md — 类型化属性、表达式、嵌套布局、作用域样式与全局样式、脚本处理
- examples/islands.md — 指令选择、
client:only、多框架岛屿、服务端岛屿、跨岛屿状态 - examples/content.md — 集合架构、查询、渲染、引用、自定义加载器、实时集合
- examples/routing.md — 静态和动态路由、rest 参数、分页、按需路由、端点、404
- examples/integrations.md — 框架集成、视图过渡、持久化、动画、Starlight
- reference.md — 项目布局、路由优先级、集合 API、适配器、渲染检查清单
适用路径
- 所有页面都是静态页面 — 默认情况。不设置
output,也不使用适配器;动态路由需要getStaticPaths()。请参阅 examples/routing.md。 - 少数页面需要请求时数据 — 保留默认设置,仅在这些页面添加
export const prerender = false,并安装服务器适配器。相关内容见同一文件,以及 reference.md 中的适配器表格。 - 大多数页面需要请求时数据 — 设置
output: "server",并通过export const prerender = true将个别页面切回静态模式。
<critical_requirements>
编写 Astro 代码前
让页面保持静态,仅在页面读取请求时数据时添加 export const prerender = false。 静态是默认模式,也是框架速度的来源。
在任何页面关闭预渲染之前,先安装服务器适配器。 否则,包含
prerender = false 的构建会失败。
框架组件需要可交互时,为其添加 client:* 指令。 如果没有指令,它会渲染为静态 HTML,且其事件处理程序永远不会运行。
在 src/content.config.ts 中使用 Zod 架构定义集合。 该架构会在构建时验证 frontmatter,并生成查询返回值的类型。
静态模式下的动态路由使用 getStaticPaths()。 按需路由可直接读取 Astro.params,无需导出该函数。
</critical_requirements>
自动检测: Astro、.astro 文件、astro.config、岛屿架构、client:load、client:visible、client:idle、client:only、client:media、server:defer、内容集合、defineCollection、defineLiveCollection、getCollection、getLiveCollection、getEntry、getLiveEntry、render、astro:content、astro:transitions、ClientRouter、getStaticPaths、Astro.props、Astro.params、Astro.cookies、Astro.redirect、prerender、astro add、@astrojs/react、@astrojs/vue、@astrojs/svelte、Starlight
适用范围:
.astro组件语法 — frontmatter、模板表达式、插槽、布局、作用域样式- 岛屿:客户端指令、水合时机、服务端岛屿、在同一页面混用 UI 框架
- 内容集合:架构、加载器、查询、渲染、跨集合引用
- 基于文件的路由:静态、动态、rest 参数、分页、API 端点
- 渲染模式:静态、按需,以及按页面控制
prerender - 视图过渡:
<ClientRouter />、过渡指令、持久化元素
其他部分另行处理:
- 岛屿内部如何渲染——已水合的组件遵循编写它的 UI 库的约定,Astro 只负责处理边界。
<style>块采用哪种 CSS 方案——这里的作用域规则由 Astro 决定;样式系统则不由 Astro 决定。- 哪种状态库用于填补跨岛屿的衔接——岛屿之间不共享树,以及模块作用域的状态库是跨越这一边界的方式,这些已在 examples/islands.md 中说明;至于选择哪种状态库,则没有规定。
- 每条路由都因用户而异的全交互式应用——这种应用适合默认按需渲染的框架,而非默认输出静态内容的框架。
Astro 是一个内容优先、默认不发送 JavaScript 的框架。页面大部分是静态 HTML;小型交互式“岛屿”各自独立地进行水合,每个岛屿只承担自身的开销。
由此可得出四个结论,它们解释了大部分 API:
- 组件默认为静态,除非另有指定——
client:*是启用交互的选择,因此交互功能的开销在调用处始终清晰可见。 - UI 库只是实现细节——React、Vue、Svelte、Solid 和 Preact 组件都只是岛屿的内容,同一页面可以包含多个岛屿。
- 内容是类型化数据——集合会在 Markdown frontmatter 与读取它的代码之间加入 Zod 模式,因此文章中的拼写错误会导致构建失败,而非页面出错。
- 渲染模式按页面设置——静态页面和按需渲染的页面可以共存于同一个项目中,这也是
prerender是导出项而非全局设置的原因。
<decision_framework>
静态渲染还是按需渲染? 如果页面需要在请求时读取 Cookie、请求头或用户专属数据,或者数据变化速度快于你愿意重新构建的频率,就需要按需渲染。其他情况都适合静态渲染——包括每小时变化的数据,因为重新构建比使用适配器更经济。
使用 .astro 还是框架组件? 如果没有客户端交互,就用 .astro——无需 JavaScript。简单交互(切换、显示/隐藏)可用 .astro 加 <script> 标签实现,这比使用框架更轻量。当交互需要框架自身的状态模型时,再使用框架组件。
使用哪个客户端指令? 模式 4 中的表格说明了每个指令的用途。client:only 是最后的选择,适用于完全无法在服务器端渲染的组件。
使用构建时集合还是实时集合? 对于每次请求都相同的内容——文章、文档、更新日志、作者简介——使用 defineCollection。只有数据必须在每次请求时保持最新(库存、定价)时才使用 defineLiveCollection;这也会使对应页面改为按需渲染。
</decision_framework>
核心模式
模式 1:Astro 组件结构
.astro 文件由 --- 分隔的仅在服务器端运行的脚本和后续的 HTML 模板组成。脚本不会发送到浏览器,因此数据获取应放在其中。
---
interface Props { title: string; description?: string }
const { title, description = "Default" } = Astro.props;
const posts = await getCollection("blog");
---
<h1>{title}</h1>
{posts.map((post) => <a href={`/blog/${post.id}`}>{post.data.title}</a>)}
<style>h1 { color: navy; }</style>
<style> 块中的样式默认限定在组件作用域内,除非标记为 is:global。
完整代码:examples/core.md
模式 2:使用插槽进行组合
<slot /> 接收父组件传入的内容。具名插槽会接收带有匹配的 slot="name" 属性的内容;<slot> 元素内的任何内容都会在未传入内容时作为回退内容显示。
<article>
<header><slot name="header"><h2>{title}</h2></slot></header>
<slot />
<footer><slot name="footer" /></footer>
</article>
完整代码:examples/core.md
模式 3:布局
布局是一个普通组件,通过默认插槽包裹页面内容;布局可以嵌套——例如,博客布局包裹基础布局,而基础布局再包裹页面。
---
const { title } = Astro.props;
---
<html lang="en">
<head><title>{title}</title></head>
<body><main><slot /></main></body>
</html>
完整代码:examples/core.md
### 模式 4:岛屿和客户端指令框架组件会渲染为静态 HTML,直到 client:* 指令对其进行水合。该指令决定水合的_时机_。
| 指令 | 水合时机 | 适用场景 |
|---|---|---|
client:load | 页面加载时立即水合 | 关键交互式 UI |
client:idle | 浏览器空闲时水合 | 优先级较低的交互 |
client:visible | 滚动进入视口时水合 | 首屏以下的内容 |
client:media | 媒体查询匹配时水合 | 响应式交互 |
client:only | 仅在客户端渲染,跳过服务端渲染 | 依赖浏览器的组件 |
<Header />
<SearchBar client:load />
<Comments client:visible />
<Analytics client:media="(max-width: 768px)" />
完整代码:examples/islands.md
模式 5:服务端岛屿
server:defer 会在每次请求时渲染组件,同时页面其余部分仍可缓存。在组件到达前,fallback 插槽会占据其位置。
<ProductInfo product={product} />
<UserReviews server:defer>
<div slot="fallback">Loading reviews...</div>
</UserReviews>
完整代码:examples/islands.md
模式 6:内容集合
集合将加载器与 Zod schema 配对。getCollection 可通过可选筛选条件查询集合,而 render() 会将条目的 Markdown 转换为组件。
// src/content.config.ts
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 };
const posts = await getCollection("blog", ({ data }) => !data.draft);
const { Content, headings } = await render(post);
实时集合是另一种机制:在 src/live.config.ts(而非 content.config.ts)中使用 defineLiveCollection 定义,通过 getLiveCollection() / getLiveEntry() 查询,且只能用于按需页面。
完整代码:examples/content.md
模式 7:基于文件的路由
src/pages/ 映射到 URL。[param] 表示单个动态路径段,[...rest] 匹配任意深度,而以下划线开头的文件不会参与路由。
src/pages/
├── index.astro → /
├── blog/[id].astro → /blog/:id
├── docs/[...slug].astro → /docs and /docs/*
└── _helpers.ts → not a route
在静态模式下,每个动态路由都会导出 getStaticPaths(),并返回 { params, props } 键值对;其中的 paginate() 会生成带编号的页面。
完整代码:examples/routing.md
模式 8:按需渲染和端点
export const prerender = false 会将页面改为在请求时渲染,此时可使用 Astro.cookies、Astro.redirect(),并返回 Response。API 端点是导出各 HTTP 方法对应处理函数的 .ts 文件,默认情况下和其他内容一样采用静态渲染。
export const prerender = false;
export const GET: APIRoute = async ({ url }) => {
const query = url.searchParams.get("q");
if (!query)
return new Response(JSON.stringify({ error: "Missing query" }), {
status: 400,
});
return new Response(JSON.stringify(await searchDatabase(query)));
};
完整代码:examples/routing.md
模式 9:视图过渡
在文档头部添加 <ClientRouter /> 可启用跨页面过渡。transition:name 会将元素配对,使其变形过渡;transition:persist 会让元素在页面导航过程中保持存在。
<head><ClientRouter /></head>
<img src={post.data.heroImage} transition:name={`hero-${post.id}`} transition:animate="slide" />
<audio controls transition:persist><source src="/music.mp3" type="audio/mp3" /></audio>
<red_flags>
需要警惕的问题
会导致运行时出错:
- 静态模式下的动态路由未定义
getStaticPaths()— 构建会失败,并提示“getStaticPaths() is required”。 - 设置了
prerender = false,但未安装服务器适配器 — 构建会失败。 - 使用
<ViewTransitions />— 该组件已在 Astro 6 中移除;请从astro:transitions导入<ClientRouter />。 - 使用
Astro.glob()— 该方法已在 Astro 6 中移除;请改用import.meta.glob()。 - 使用
import { z } from "astro:content"— 该导入已在 Astro 6 中移除;请从astro/zod导入z。这里使用的是 Zod 4,因此z.string().email()现在应写作z.email()。 getStaticPaths()的参数为数字 — Astro 6 要求参数为字符串;请用String(id)包装。- 使用
astro.config.cjs— Astro 6 要求使用 ESM(.mjs或.ts)。 - 将函数、类实例或符号作为属性传递给已水合的组件 — 孤岛组件的属性必须可序列化。
- 在 frontmatter 分隔符之外读取
Astro.props,或将.astro组件导入框架组件 — 这两种情况下都无法使用它。 - 在低于 22.12.0 的 Node 版本上使用 Astro 6 — 不受支持。
容易引发意外的行为:
- 对所有内容使用
client:load会破坏架构设计;没有交互功能的组件不需要任何指令。 client:only会跳过服务器端渲染,因此爬虫在 HTML 中看不到任何内容 — 只要组件可以在服务器端渲染,就优先使用client:load。- 在
<script>标签中获取数据会形成客户端瀑布流;frontmatter 在服务器端运行,不会产生这种开销。 - 未定义 schema 的集合会同时失去构建时验证和生成的类型。
- 对以静态内容为主的网站设置
output: "server",会放弃默认配置为那些不需要此设置的页面提供的缓存。 - 未经过筛选的
getCollection会同时返回草稿和已发布条目。 <style>默认采用作用域样式 — 请有意使用<style is:global>或:global()选择器。<script>会被打包并去重;使用is:inline会跳过此处理,因此脚本会在每次 View Transitions 导航时重新运行。client:visible会等待 IntersectionObserver,因此从未出现在屏幕上的组件永远不会进行水合。Astro.redirect()仅适用于按需渲染的页面;静态页面无法在收到请求时重定向。- 孤岛组件之间不共享状态 — 跨孤岛通信需要使用你自行引入的存储或事件。
transition:persist需要在两个页面上使用相同的transition:name,否则元素会被重新创建。- 集合条目的 id 默认来自文件名,除非 frontmatter 覆盖了它。
- 实时集合无法渲染 MDX,且
import.meta.env会在构建时内联 — 请从process.env读取运行时密钥。 - 当 params 条目传入
undefined时,[...slug]路由也会匹配其自身的基础路径。