关于 astro
Astro 是用于构建博客、文档和作品集等内容型网站的 Web 框架技能。它涵盖文件路由、内容集合,以及仅为交互组件选择性注水,让大多数页面保持静态。
主要功能
- 01基于文件的页面路由
- 02类型化 Markdown 和 MDX 内容集合
- 03选择性注水组件
- 04支持多种 UI 框架
- 05静态输出及可选 SSR
适用场景
- 01构建文档或博客网站
- 02制作内容型营销页面
- 03为静态页面添加选择性交互
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill astro安装后在 Claude Code 中输入/astro确认已加载。
详细介绍
Astro Web Framework
概述
Astro 是一个专为内容丰富的网站设计的 Web 框架,适用于博客、文档、作品集、营销网站和电子商务网站。它的核心创新是群岛架构:默认情况下,Astro 不会向浏览器发送任何 JavaScript。交互组件会作为相互隔离的“岛屿”按需进行水合。Astro 支持在同一项目中同时使用 React、Vue、Svelte、Solid 和其他 UI 框架,让你能为每个组件选择合适的工具。
何时使用此技能
- 用于构建博客、文档网站、营销页面或作品集
- 用于将性能和 Core Web Vitals 作为首要目标
- 用于使用 Markdown 或 MDX 文件构建以内容为主的项目
- 用于希望生成 SSG(静态)输出,并可为动态路由选择性启用 SSR
- 用于用户询问
.astro文件、Astro.props、内容集合或client:指令时
工作原理
步骤 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 component support
npx astro add mdx # MDX support
npx astro add sitemap # Auto sitemap.xml
npx astro add vercel # Vercel SSR adapter
项目结构:
src/
pages/ ← File-based routing (.astro, .md, .mdx)
layouts/ ← Reusable page shells
components/ ← UI components (.astro, .tsx, .vue, etc.)
content/ ← Type-safe content collections (Markdown/MDX)
styles/ ← Global CSS
public/ ← Static assets (copied as-is)
astro.config.mjs ← Framework config
步骤 2:Astro 组件语法
.astro 文件顶部有一个代码围栏(仅在服务端运行),下方是模板:
---
// src/components/Card.astro
// This block runs on the server ONLY — never in the browser
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>
/* Scoped to this component automatically */
.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 (dynamic)
src/pages/blog/[...path].astro → /blog/* (catch-all)
使用 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 />
步骤 3–4 和 RSS 中的内容集合示例使用的是 Astro 5 之前的旧版内容 API(src/content/config.ts、post.slug、post.render())。不要在新项目中未经修改就照搬这些示例:请根据已安装的主版本选择对应 API,按需迁移到内容加载器并使用 render(),同时在动态页面中导入 getCollection。Tailwind 集成的设置也取决于其主版本。请参阅 Astro 5 迁移指南。
步骤 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 框架组件会渲染为不含 JS 的静态 HTML。使用 client: 指令进行水合:
---
import Counter from '../components/Counter.tsx'; // React component
import VideoPlayer from '../components/VideoPlayer.svelte';
---
<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 = 'My Astro Site' } = Astro.props;
---
<html lang="en">
<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="About Us">
<h1>About Us</h1>
<p>Welcome to our company...</p>
</BaseLayout>
步骤 7:SSR 模式(按需渲染)
通过设置适配器启用动态页面的 SSR:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
output: 'static', // Astro 5+: prerender by default, opt routes out below
adapter: vercel(),
});
使用 export const prerender = false 为单个页面启用 SSR。
示例
示例 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: 'My Blog',
description: 'Latest posts',
site: context.site,
items: posts.map(post => ({
title: post.data.title,
pubDate: post.data.date,
link: `/blog/${post.slug}/`,
})),
});
}
订阅处理器只是一个不完整的应用示例:请定义 addToNewsletter,在静态模式下将此路由设为不预渲染,限制并验证 JSON 和电子邮件输入,处理下游错误,并在公开此功能前,添加适合部署环境的授权、滥用防护和 CSRF 防护。
示例 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: 'Email required' }), {
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<Array<{ id: string; title: string }>>([]);
async function search(e: React.FormEvent) {
e.preventDefault();
const data = await fetch(`/api/search?q=${encodeURIComponent(query)}`).then(r => {
if (!r.ok) throw new Error(`Search failed: ${r.status}`);
return r.json();
});
setResults(data);
}
return (
<form onSubmit={search}>
<input value={query} onChange={e => setQuery(e.target.value)} />
<button type="submit">Search</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,以减少初始 JS - ✅ 使用
import.meta.env获取环境变量——为公开变量添加PUBLIC_前缀 - ✅ 添加来自
astro:transitions的<ClientRouter />,无需构建完整的 SPA 即可实现流畅的页面导航 - ❌ 不要为每个组件都使用
client:load——这会削弱 Astro 的性能优势 - ❌ 不要将机密放在
.astrofrontmatter 中,尤其是这些内容会用于面向客户端的模板时 - ❌ 静态模式下,动态路由不要省略
getStaticPaths——否则构建会失败
安全与注意事项
- Frontmatter 会在构建时或服务器上执行,但插入 HTML、props 或脚本中的值会暴露给浏览器。不要将机密放入渲染输出中。
- 仅将
import.meta.env.PUBLIC_*用于非敏感值。私有环境变量不会自动暴露给客户端模块,但将其值插入渲染输出仍会泄露。 - 使用 SSR 模式时,在执行数据库查询或 API 调用前,验证所有
Astro.request输入。 - 使用
set:html渲染用户提供的内容前,请对其进行清理——它会绕过自动转义。
常见问题
-
问题: React/Vue 组件中的 JavaScript 未在浏览器中运行 解决方案: 添加
client:指令(client:load、client:visible等)——否则组件只会渲染为静态 HTML。 -
问题: 开发期间内容更新后,
getStaticPaths数据仍然过时 解决方案: Astro 的开发服务器会监视内容文件——如果对content/config.ts的更改未生效,请重启服务器。 -
问题:
Astro.props的类型是any——没有自动补全 解决方案: 在 frontmatter 中定义Props接口或类型,Astro 会自动推断类型。 -
问题:
.astro组件中的 CSS 影响了其他组件 解决方案:.astro<style>标签中的样式会自动限定作用域。仅在有意为子元素设置样式时使用:global()。
相关技能
@sveltekit— 需要具有响应式 UI 的全栈框架时使用(相较于 Astro 对内容的侧重)@nextjs-app-router-patterns— 需要以 React 为核心的全栈框架时使用@tailwind-patterns— 使用 Tailwind CSS 为 Astro 网站设置样式@progressive-web-app— 为 Astro 网站添加 PWA 功能
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必需的输入、权限、安全边界或成功标准,请暂停并要求澄清。