Astro 网页框架
概述
此公共摄入副本将 https://github.com/sickn33/antigravity-awesome-skills 中的 plugins/antigravity-awesome-skills/skills/astro 打包为 Omni Skills 原生编辑形状,同时不隐藏其来源。
当操作者需要上游工作流、支持文件和仓库上下文保持完整,而公共验证器和私有增强器继续正常的下游流程时使用。
此摄入保持复制过来的上游文件不变,并使用 metadata.json 中的 external_source 块和 ORIGIN.md 作为审查的来源锚点。
Astro 网页框架
以下为未完美映射到公共标题的导入源章节,它们仍保留在下面或支持文件中。值得注意的导入章节:工作原理、安全与注意事项、常见陷阱、局限性。
何时使用此技能
将此章节用作触发过滤条件。它应在操作者加载文件、运行命令或发起拉取请求之前明确激活边界。
- 当构建博客、文档站点、营销页面或作品集时使用
- 当性能和核心网页指标是首要优先级时使用
- 当项目包含大量 Markdown 或 MDX 内容时使用
- 当您需要 SSG(静态)输出,并可选 SSR 用于动态路由时使用
- 当用户询问关于 .astro 文件、Astro.props、内容集合或 client: 指令时使用
- 当请求明确匹配导入源意图时使用:使用 Astro 构建内容驱动的网站——默认零 JavaScript、孤岛架构、多框架组件和 Markdown/MDX 支持。
操作表
| 状态 | 起始点 | 为何重要 |
|---|---|---|
| 首次使用 | metadata.json | 通过 external_source 块确认仓库、分支、提交和导入路径,然后再接触复制的工作流 |
| 来源审查 | ORIGIN.md | 为审查者提供导入源的纯文本审计线索 |
| 工作流执行 | SKILL.md | 从能实质性改变执行的最小复制文件开始 |
| 辅助上下文 | SKILL.md | 在不加载整个包的情况下,添加下一个最相关的复制源文件 |
| 移交决策 | ## 相关技能 | 当任务偏移时,帮助操作者切换到更强大的原生技能 |
工作流
此工作流在编辑性和操作性上兼具意图。它保持导入源对操作者有用,同时满足下游增强器流程的公共摄入标准。
- 确认用户目标、导入工作流的范围,以及此技能是否仍是任务的正确路由器。
- 在加载任何复制的上游支持文件之前先阅读概述和来源文件。
- 仅加载对当前请求结果有实质性改变的参考资料、示例、提示或脚本。
- 执行上游工作流,同时在工作笔记中明确保持来源和源边界。
- 根据上游期望以及您能在复制文件中指出的证据来验证结果。
- 当工作移出此导入工作流的中心区域时,升级或移交给相关技能。
- 在合并或关闭之前,记录使用了什么、改变了什么,以及审查者仍需验证什么。
导入工作流笔记
导入:概述
Astro 是一个为内容丰富的网站设计的网页框架——博客、文档、作品集、营销网站和电子商务。其核心创新是孤岛架构:默认情况下,Astro 向浏览器发送零 JavaScript。交互式组件作为独立的“孤岛”按需水合。Astro 在同一项目中同时支持 React、Vue、Svelte、Solid 和其他 UI 框架,让您为每个组件选择合适的工具。
导入:工作原理
步骤 1:项目设置
npm create astro@latest my-site
cd my-site
npm install
npm run dev
根据需要添加集成:
npx astro add tailwind # Tailwind CSS
npx astro add react # React 组件支持
npx astro add mdx # MDX 支持
npx astro add sitemap # 自动 sitemap.xml
npx astro add vercel # Vercel SSR 适配器
项目结构:
src/
pages/ ← 基于文件的路由 (.astro, .md, .mdx)
layouts/ ← 可复用的页面模板
components/ ← UI 组件 (.astro, .tsx, .vue 等)
content/ ← 类型安全的内容集合 (Markdown/MDX)
styles/ ← 全局 CSS
public/ ← 静态资源(按原样复制)
astro.config.mjs ← 框架配置
步骤 2:Astro 组件语法
.astro 文件在顶部有一个代码栅栏(仅服务端),下方是模板:
---
// src/components/Card.astro
// 此块仅在服务端运行——绝不在浏览器中
interface Props {
title: string;
href: string;
description: string;
}
const { title, href, description } = Astro.props;
---
<article class="card">
<h2><a href={href}>{title}</a></h2>
<p>{description}</p>
</article>
<style>
/* 自动作用于当前组件 */
.card { border: 1px solid #eee; padding: 1rem; }
</style>
步骤 3:基于文件的页面和路由
src/pages/index.astro → /
src/pages/about.astro → /about
src/pages/blog/[slug].astro → /blog/:slug (动态)
src/pages/blog/[...path].astro → /blog/* (通配)
带有 getStaticPaths 的动态路由:
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<h1>{post.data.title}</h1>
<Content />
步骤 4:内容集合
内容集合为您提供 Markdown 和 MDX 文件的类型安全访问:
// src/content/config.ts
import { z, defineCollection } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog'))
.filter(p => !p.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
<ul>
{posts.map(post => (
<li>
<a href={`/blog/${post.slug}`}>{post.data.title}</a>
<time>{post.data.date.toLocaleDateString()}</time>
</li>
))}
</ul>
步骤 5:孤岛——选择性水合
默认情况下,UI 框架组件渲染为无 JavaScript 的静态 HTML。使用 client: 指令进行水合:
---
import Counter from '../components/Counter.tsx'; // React 组件
import VideoPlayer from '../components/VideoPlayer.svelte';
---
<!-- 静态 HTML——不向浏览器发送 JavaScript -->
<Counter initialCount={0} />
<!-- 页面加载时立即水合 -->
<Counter initialCount={0} client:load />
<!-- 当组件滚动到视图中时水合 -->
<VideoPlayer src="/demo.mp4" client:visible />
<!-- 仅当浏览器空闲时水合 -->
<Analytics client:idle />
<!-- 仅当特定媒体查询匹配时水合 -->
<MobileMenu client:media="(max-width: 768px)" />
步骤 6:布局
---
// src/layouts/BaseLayout.astro
interface Props {
title: string;
description?: string;
}
const { title, description = '我的 Astro 站点' } = Astro.props;
---
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>{title}</title>
<meta name="description" content={description} />
</head>
<body>
<nav>...</nav>
<main>
<slot /> <!-- 页面内容在此渲染 -->
</main>
<footer>...</footer>
</body>
</html>
---
// src/pages/about.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="关于我们">
<h1>关于我们</h1>
<p>欢迎来到我们的公司...</p>
</BaseLayout>
步骤 7:SSR 模式(按需渲染)
通过设置适配器为动态页面启用 SSR:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'hybrid', // 'static' | 'server' | 'hybrid'
adapter: vercel(),
});
使用 export const prerender = false 将单个页面选择为 SSR。
示例
示例 1:直接请求上游工作流
使用 @astro-v2 处理 <任务>。从复制的上游工作流开始,仅加载能改变结果的脚本,并在答案中保持来源可见。
解释: 当操作者需要导入的工作流但不需要整个仓库时,这是最安全的起点。
示例 2:请求基于来源的审查
根据 metadata.json 和 ORIGIN.md 审查 @astro-v2,然后解释您会首先加载哪些复制的上游文件以及原因。
解释: 在需要精确、可审计的来源说明和文件选择时,在审查或故障排除之前使用。
示例 3:在执行前缩小复制的支持文件范围
使用 @astro-v2 处理 <任务>。仅加载能改变结果的复制参考资料、示例或脚本,并在继续前明确命名这些文件。
解释: 这使技能保持与渐进披露一致,而不是默认加载整个复制包。
示例 4:构建审查包
使用复制的上游文件加上来源审查 @astro-v2,然后在合并前总结任何差距。
解释: 当拉取请求等待人工审查且您需要可重复的审计包时,此方法很有用。
导入使用笔记
导入:示例
示例 1:带 RSS 源的博客
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
export async function GET(context) {
const posts = await getCollection('blog');
return rss({
title: '我的博客',
description: '最新文章',
site: context.site,
items: posts.map(post => ({
title: post.data.title,
pubDate: post.data.date,
link: `/blog/${post.slug}/`,
})),
});
}
示例 2:API 端点 (SSR)
// src/pages/api/subscribe.ts
import type { APIRoute } from 'astro';
export const POST: APIRoute = async ({ request }) => {
const { email } = await request.json();
if (!email) {
return new Response(JSON.stringify({ error: '需要邮箱' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
await addToNewsletter(email);
return new Response(JSON.stringify({ success: true }), { status: 200 });
};
示例 3:React 组件作为孤岛
// src/components/SearchBox.tsx
import { useState } from 'react';
export default function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
async function search(e: React.FormEvent) {
e.preventDefault();
const data = await fetch(`/api/search?q=${query}`).then(r => r.json());
setResults(data);
}
return (
<form onSubmit={search}>
<input value={query} onChange={e => setQuery(e.target.value)} />
<button type="submit">搜索</button>
<ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>
</form>
);
}
---
import SearchBox from '../components/SearchBox.tsx';
---
<!-- 立即水合——此孤岛是可交互的 -->
<SearchBox client:load />
最佳实践
将生成的公共技能视为上游仓库的可审查包装层。目标是保持来源明确,并仅加载那些能实质改善执行的复制源材料。
- ✅ 保持大多数组件为静态 .astro 文件——仅对必须交互的部分进行水合
- ✅ 对所有 Markdown/MDX 内容使用内容集合——您将获得类型安全和自动验证
- ✅ 对于视口以下的组件,优先使用 client:visible 而非 client:load,以减少初始 JavaScript
- ✅ 使用 import.meta.env 处理环境变量——公共变量前加 PUBLIC_ 前缀
- ✅ 添加来自 astro:transitions 的 ,以实现无需完整 SPA 的流畅页面导航
- ❌ 不要在每个组件上都使用 client:load——这会抵消 Astro 的性能优势
- ❌ 不要在用于客户端模板的 .astro frontmatter 中放置秘密
导入操作笔记
导入:最佳实践
- ✅ 保持大多数组件为静态
.astro文件——仅对必须交互的部分进行水合 - ✅ 对所有 Markdown/MDX 内容使用内容集合——您将获得类型安全和自动验证
- ✅ 对于视口以下的组件,优先使用
client:visible而非client:load,以减少初始 JavaScript - ✅ 使用
import.meta.env处理环境变量——公共变量前加PUBLIC_前缀 - ✅ 添加来自
astro:transitions的<ViewTransitions />,以实现无需完整 SPA 的流畅页面导航 - ❌ 不要在每个组件上都使用
client:load——这会抵消 Astro 的性能优势 - ❌ 不要在用于客户端模板的
.astrofrontmatter 中放置秘密 - ❌ 不要在静态模式下跳过动态路由的
getStaticPaths——构建将会失败
故障排除
问题:操作者跳过了导入上下文并过于笼统地回答
症状: 结果忽略了 plugins/antigravity-awesome-skills/skills/astro 中的上游工作流,未提及来源,或未使用任何复制的源文件。
解决方案: 重新打开 metadata.json、ORIGIN.md 和最相关的复制上游文件。首先检查 external_source 块,然后在继续之前重申来源。
问题:导入的工作流在审查时感觉不完整
…
[截断并保持原样,因为原始内容被截断]