Communitygithub.com

Astro 框架操作指南

Astro 框架模式:群岛架构、内容集合、渲染策略与部署。适用于:Astro、群岛架构、内容集合、Astro Cloudflare、视图过渡、部分水合、Astro 项目、SSG、SSR、混合渲染、Astro 适配器。

Astro 框架操作指南 是什么?

该技能为 Astro 框架开发者提供了全面、实用的操作指南,涵盖从项目初始化到生产部署的整个生命周期。它围绕 Astro 的核心特性(如群岛架构、内容集合和视图过渡)组织了详细的技术决策树和快速参考表,帮助开发者在 SSG、SSR 和混合渲染模式之间做出正确选择,并优化性能。 技能内容还提供了 Astro 项目中常见陷阱的解决方案清单,例如水合不匹配错误、动态路由 404 问题以及环境变量访问错误。此外,它还包含了一个生产安全清单,指导开发者配置 CSP 头、验证主机头以及管理密钥。该技能作为 Astro 开发的权威参考,帮助开发团队编写更稳定、可维护且高性能的 Astro 应用。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0xDarkMatter/claude-mods/tree/HEAD/skills/astro-ops

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Astro 操作指南

事实确认截至 2026-07。

Astro 框架开发的综合模式:群岛架构、内容集合、渲染策略、视图过渡和多平台部署。

渲染策略决策树

Which rendering strategy?
│
├─ Is content mostly static (blog, docs, marketing)?
│  ├─ YES → Does it change less than daily?
│  │  ├─ YES → SSG (output: 'static')
│  │  │        Fastest TTFB, CDN-cacheable, zero runtime cost
│  │  └─ NO  → Hybrid (output: 'hybrid')
│  │           Default static + opt-in SSR per route
│  └─ NO  → Does every page need personalization?
│     ├─ YES → SSR (output: 'server')
│     │        Dynamic per-request, auth-aware, real-time data
│     └─ NO  → Hybrid (output: 'hybrid')
│              Static shell + server islands for dynamic parts
│
├─ Does the app need real-time interactivity (dashboard, SPA)?
│  ├─ YES → Is it a full SPA with client-side routing?
│  │  ├─ YES → Consider React/Vue SPA instead, or Astro + client:only
│  │  └─ NO  → Hybrid + islands architecture
│  │           Interactive islands in static pages
│  └─ NO  → SSG (output: 'static')
│
├─ Build time concerns (>10k pages)?
│  ├─ YES → Hybrid with on-demand rendering
│  │        Prerender popular pages, SSR the long tail
│  └─ NO  → SSG handles it fine
│
└─ Need edge computing (low latency globally)?
   ├─ YES → SSR + Cloudflare/Vercel Edge adapter
   └─ NO  → SSR + Node adapter or SSG

配置

// astro.config.mjs
import { defineConfig } from 'astro/config';

// SSG (default) - all pages prerendered at build time
export default defineConfig({
  output: 'static',
});

// SSR - all pages rendered on request
export default defineConfig({
  output: 'server',
  adapter: cloudflare(), // or vercel(), netlify(), node()
});

// Hybrid - static default, opt-in SSR per page
export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare(),
});
---
// In hybrid mode, opt OUT of prerendering for specific pages:
export const prerender = false;
// In SSR mode, opt IN to prerendering:
export const prerender = true;
---

群岛架构快速参考

指令水合时机发送 JS使用场景
client:load页面加载后立即执行完整包首屏交互(导航栏、Hero CTA)
client:idle页面空闲后 (requestIdleCallback)完整包非首屏交互(评论表单、聊天)
client:visible滚动到视口时完整包远离首屏的组件(页脚小部件、轮播图)
client:media媒体查询匹配时完整包仅移动端导航、响应式组件
client:only="react"立即执行,跳过 SSR完整包无法 SSR 的组件(Canvas、WebGL)
(无)从不 - 仅静态 HTML零 JS静态内容、卡片、标题
---
import NavBar from '../components/NavBar.tsx';
import CommentForm from '../components/CommentForm.tsx';
import ImageCarousel from '../components/ImageCarousel.svelte';
import MobileMenu from '../components/MobileMenu.vue';
import ThreeScene from '../components/ThreeScene.tsx';
---

<!-- Loads immediately - critical interactivity -->
<NavBar client:load />

<!-- Loads after page is idle - non-critical -->
<CommentForm client:idle />

<!-- Loads when scrolled into view - lazy -->
<ImageCarousel client:visible />

<!-- Loads only on mobile -->
<MobileMenu client:media="(max-width: 768px)" />

<!-- Client-only, no SSR (WebGL can't run on server) -->
<ThreeScene client:only="react" />

内容集合快速入门

定义 Schema

// src/content.config.ts (Astro 5) or src/content/config.ts (Astro 4)
import { defineCollection, z, reference } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string().max(160),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: reference('authors'), // Reference another collection
  }),
});

const authors = defineCollection({
  loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
  schema: z.object({
    name: z.string(),
    avatar: z.string(),
    bio: z.string(),
    socials: z.object({
      twitter: z.string().optional(),
      github: z.string().optional(),
    }).optional(),
  }),
});

export const collections = { blog, authors };

查询集合

---
import { getCollection, getEntry } from 'astro:content';

// Get all non-draft blog posts, sorted by date
const posts = (await getCollection('blog', ({ data }) => !data.draft))
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

// Get a single entry
const post = await getEntry('blog', 'my-first-post');

// Resolve a reference
const author = await getEntry(post.data.author);

// Render content
const { Content, headings } = await post.render();
---

<Content />

内容集合 vs 外部 CMS

标准内容集合外部 CMS (Payload 等)
内容类型本地 Markdown/MDX、文档、博客关系数据模型
作者开发者 (版本控制)编辑者 (管理界面、多用户认证)
验证通过 Zod 在构建时实现类型安全CMS 端 Schema + API 合约
更新频率随站点一起部署独立于部署
API 需求无 (构建时查询)REST/GraphQL (供其他消费者使用)
工作流程Git PR、简单审阅编辑工作流、草稿、角色

经验法则:从内容集合开始;仅当非开发人员需要在无需部署的情况下发布内容,或者内容确实是关系型时,才使用 CMS。

项目结构参考

project-root/
├── astro.config.mjs          # Astro configuration
├── tsconfig.json              # TypeScript config (extends astro/tsconfigs)
├── package.json
├── public/                    # Static assets (copied as-is)
│   ├── favicon.svg
│   ├── robots.txt
│   └── og-image.png
├── src/
│   ├── pages/                 # File-based routing
│   │   ├── index.astro        # → /
│   │   ├── about.astro        # → /about
│   │   ├── blog/
│   │   │   ├── index.astro    # → /blog
│   │   │   └── [slug].astro   # → /blog/:slug (dynamic)
│   │   ├── api/
│   │   │   └── search.ts      # → /api/search (API endpoint)
│   │   └── [...slug].astro    # → catch-all/404
│   ├── layouts/
│   │   ├── BaseLayout.astro   # HTML shell, <head>, global styles
│   │   └── BlogPost.astro     # Blog post layout
│   ├── components/
│   │   ├── Header.astro       # Static Astro component
│   │   ├── Footer.astro
│   │   ├── NavBar.tsx         # React island
│   │   └── Counter.svelte     # Svelte island
│   ├── content/               # Content collections source files
│   │   ├── blog/
│   │   │   ├── post-one.md
│   │   │   └── post-two.mdx
│   │   └── authors/
│   │       └── jane.json
│   ├── content.config.ts      # Collection schemas (Astro 5)
│   ├── middleware.ts           # Request/response middleware
│   ├── styles/
│   │   └── global.css
│   └── lib/                   # Shared utilities
│       ├── utils.ts
│       └── constants.ts
└── .env                       # Environment variables

视图过渡快速参考

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

过渡指令

<!-- Persist element across pages (keeps state, avoids re-render) -->
<audio transition:persist id="player">
  <source src="/music.mp3" />
</audio>

<!-- Named transition for animation pairing -->
<img transition:name="hero" src={post.heroImage} />

<!-- Custom animation -->
<div transition:animate="slide">Content</div>
<div transition:animate="fade">Content</div>
<div transition:animate="none">No animation</div>

<!-- Persist with name (for multiple persistent elements) -->
<video transition:persist="media-player" />

生命周期事件

<script>
  document.addEventListener('astro:before-preparation', (e) => {
    // Before new page is fetched - cancel navigation, show loading
  });

  document.addEventListener('astro:after-preparation', (e) => {
    // New page fetched, before swap
  });

  document.addEventListener('astro:before-swap', (e) => {
    // Customize DOM swap behavior
  });

  document.addEventListener('astro:after-swap', () => {
    // DOM updated - reinitialize scripts
  });

  document.addEventListener('astro:page-load', () => {
    // Page fully loaded (fires on initial + every navigation)
    // Use this instead of DOMContentLoaded with View Transitions
  });
</script>

前进/后退处理

// astro.config.mjs
export default defineConfig({
  prefetch: {
    prefetchAll: true,         // Prefetch all links on hover
    defaultStrategy: 'hover',  // 'hover' | 'tap' | 'viewport' | 'load'
  },
});
<!-- Per-link prefetch control -->
<a href="/about" data-astro-prefetch>Prefetch on hover (default)</a>
<a href="/blog" data-astro-prefetch="viewport">Prefetch when visible</a>
<a href="/contact" data-astro-prefetch="load">Prefetch immediately</a>
<a href="/external" data-astro-prefetch="false">No prefetch</a>

部署决策树

Where to deploy?
│
├─ Need edge computing + Cloudflare ecosystem (KV, D1, R2)?
│  └─ Cloudflare Pages/Workers
│     Adapter: @astrojs/cloudflare
│     Best for: Global edge, Workers bindings, cost-effective
│
├─ Need serverless + Vercel ecosystem (ISR, analytics)?
│  └─ Vercel
│     Adapter: @astrojs/vercel
│     Best for: Next.js migration, image optimization, ISR
│
├─ Need serverless + Netlify ecosystem (forms, identity)?
│  └─ Netlify
│     Adapter: @astrojs/netlify
│     Best for: JAMstack, built-in forms, split testing
│
├─ Need full server control (Docker, custom runtime)?
│  └─ Node.js (standalone or Express/Fastify)
│     Adapter: @astrojs/node
│     Best for: Self-hosted, WebSocket, long-running processes
│
└─ Pure static site (no SSR needed)?
   └─ Any static host (GitHub Pages, S3, Cloudflare Pages)
      No adapter needed, output: 'static'
      Best for: Blogs, docs, marketing sites

适配器安装

# Cloudflare
npx astro add cloudflare

# Vercel
npx astro add vercel

# Netlify
npx astro add netlify

# Node.js
npx astro add node

常见陷阱

陷阱原因修复
水合不匹配错误服务器 HTML 与客户端渲染不同(日期、随机 ID、浏览器 API)对浏览器相关的组件使用 client:only,或确保确定性渲染
客户端中 import.meta.env 未定义只有 PUBLIC_ 前缀的变量会暴露给客户端代码重命名为 PUBLIC_MY_VAR 或通过 props 从服务器传递
SSG 中动态路由 404getStaticPaths() 未返回所有可能的参数确保 getStaticPaths() 返回每个有效的路径,或切换到混合/SSR
图片未优化使用 <img> 而不是 Astro 的 <Image /> 组件从 astro:assets 导入:import { Image } from 'astro:assets' 并对 src 使用本地导入
无适配器的 SSR 失败output: 'server' 或 'hybrid' 需要部署适配器安装适配器:npx astro add cloudflare(或 vercel、netlify、node)
MDX 组件未渲染自定义组件未传递给 MDX 内容通过 <Content components={{ MyComponent }} /> 传递组件,或使用 astro.config.mjs 的 MDX 配置
内容集合 Schema 更改未反映类型生成已缓存,.astro 类型过期运行 astro sync 重新生成类型,重启开发服务器
在 Astro 组件上使用 client:*客户端指令仅适用于框架组件(React、Vue、Svelte)Astro 组件是纯静态的;将交互部分提取到框架组件中
document/window 未定义服务器端代码无法访问浏览器全局变量使用 if (typeof window !== 'undefined') 进行保护,或将其移至 client:only
组件间样式泄露使用全局 CSS 而不是作用域样式使用 <style>(在 .astro 中默认作用域)或有意使用 <style is:global>
视图过渡破坏脚本DOMContentLoaded 在视图过渡中只触发一次改用 astro:page-load 事件,该事件在每次导航时都会触发
生产环境中缺少环境变量未加载 .env 或平台环境变量未配置在 astro.config.mjs 中使用 envField 进行验证;在平台仪表盘中设置变量

生产安全清单

对于每个生产部署,请处理:

  • CSP 头 - 配置严格的 Content-Security-Policy(请参阅 references/deployment.md 中的中间件模式)
  • 远程图片限制 - 强制执行明确的 image.domains/remotePatterns 允许列表;切勿从用户输入中派生图片 URL(SSRF 风险)
  • 主机头验证 - 在中间件中验证请求主机是否与预期域匹配(仅 SSR/混合)
  • 密钥管理 - 在 Cloudflare 上,使用 Workers Bindings (wrangler secret put),而不是将环境变量嵌入代码中;其他地方使用平台密钥存储
  • 仅 HTTPS - 确保所有外部资源(脚本、图片、字体)通过 HTTPS 加载
  • 输入验证 - 在 SSR 上下文中清理所有用户输入(查询参数、表单体、Cookie)

参考文件

文件内容行数
references/content-collections.mdSchema patterns, Zod types, querying, MDX, content layer API, migrations~500
`references/islands-rendering

…

[已截断供翻译]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相关技能