mcpField
W
web-meta-framework-astro

web-meta-framework-astro

github.com/agents-inc开发2639 次安装2 天前更新
代码仓库

关于 web-meta-framework-astro

面向 Astro 开发者的模式指南,涵盖组件、内容集合、路由与渲染方式。重点说明默认静态输出、按需交互岛屿及逐页渲染控制,并包含 Astro 6 的迁移注意事项。

主要功能

  1. 01Astro 组件与布局模式
  2. 02交互岛屿与客户端指令
  3. 03带 Zod 校验的内容集合
  4. 04静态及按需路由
  5. 05页面级渲染控制
  6. 06Astro 6 注意事项

适用场景

  1. 01为 Astro 页面选择静态或按需渲染
  2. 02为组件配置岛屿水合时机
  3. 03定义并查询类型化 Markdown 内容
  4. 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.yaml
reference.md
examples/
content.md
core.md
integrations.md
islands.md
routing.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:

  1. 组件默认为静态,除非另有指定——client:* 是启用交互的选择,因此交互功能的开销在调用处始终清晰可见。
  2. UI 库只是实现细节——React、Vue、Svelte、Solid 和 Preact 组件都只是岛屿的内容,同一页面可以包含多个岛屿。
  3. 内容是类型化数据——集合会在 Markdown frontmatter 与读取它的代码之间加入 Zod 模式,因此文章中的拼写错误会导致构建失败,而非页面出错。
  4. 渲染模式按页面设置——静态页面和按需渲染的页面可以共存于同一个项目中,这也是 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>

完整代码:examples/integrations.md


<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] 路由也会匹配其自身的基础路径。

评论

支持 Markdown