Astro

技术栈
前端框架
ssgmpaislandscontentzero-js

概览

Astro 技术栈概览

Astro 是专注于内容驱动网站的现代静态站点生成器。核心理念是"默认零 JS"——服务器端渲染 HTML,仅在需要交互时通过**岛屿架构(Islands Architecture)**注入 JavaScript。

Astro 是什么?

  • 多框架融合的静态站点生成器(SSG)
  • 支持 Vue/React/Svelte/Solid 组件混合使用
  • .astro 单文件组件(类似 Vue SFC)
  • 默认输出纯 HTML + CSS,零 JS

解决什么问题?

  • 博客/文档站/营销站等内容型网站
  • 需要 SEO 和首屏加载速度
  • 毕设作品展示站、技术文档站
  • 多个框架组件在同一页面共存

关键特性:

  • 岛屿架构(Islands):交互组件按需水合
  • Content Collections:Markdown/MDX 内容管理
  • 内置图片优化
  • 支持多种部署:Vercel / Netlify / Cloudflare
  • Astro DB(内置数据库)

安装

环境准备

  • 操作系统:macOS / Linux / Windows
  • Node.js:>= 18.14.1(推荐 20 LTS)
  • 包管理器:npm / yarn / pnpm
  • 编辑器:VS Code(安装官方 Astro 扩展,支持 .astro 语法高亮)

安装命令

创建新项目

# 使用官方脚手架
npm create astro@latest

# 交互式选择:
# ┌  Where should we create your new project?
# │  ./my-astro-site
# ├  How would you like to start?
# │  ● Include sample files (推荐)
# │  ○ Empty
# ├  Do you plan to write TypeScript?
# │  ● Yes
# ├  Install dependencies?
# │  ● Yes

使用特定模板

# 博客模板
npm create astro@latest -- --template blog

# 文档站模板
npm create astro@latest -- --template docs

# 集成 Tailwind
npm create astro@latest -- --template with-tailwindcss

为现有项目添加集成

# 添加 React 支持
npx astro add react

# 添加 Vue 支持
npx astro add vue

# 添加 Tailwind
npx astro add tailwind

常见安装问题

1. astro 命令未找到

  • 解决:使用 npx astro 或在 package.json 中通过 npm run dev 调用

2. 与其他框架组件混用时报错

  • 解决:确保已通过 npx astro add <framework> 安装对应集成;.astro 文件中使用 client:load 等指令标记水合客户端组件

3. 构建时内存不足

  • 解决:Astro 构建通常比 Gatsby 轻量得多。如遇问题:NODE_OPTIONS=--max-old-space-size=4096 npx astro build

4. 图片优化不生效

  • 解决:Astro 内置 <Image />(来自 astro:assets),确保图片在 src/ 下;远程图片需先在 astro.config.mjs 中配置域名白名单

示例

Astro 个人博客站

目标

用 Astro 搭建一个最小博客站,展示 .astro 组件、Markdown 内容集合和岛屿架构。

完整代码

1. 项目结构

my-blog/
├── astro.config.mjs
├── src/
│   ├── pages/
│   │   └── index.astro
│   ├── content/
│   │   └── posts/
│   │       ├── post-1.md
│   │       └── post-2.md
│   └── components/
│       └── PostCard.astro

2. src/content/posts/post-1.md

---
title: '毕设选题心得'
date: 2024-03-15
description: '如何选择一个合适的毕业设计题目'
---

## 选题三原则

1. **兴趣驱动**:选你真正感兴趣的方向
2. **技术可行**:确保技术栈在掌握范围内
3. **答辩加分**:有可视化展示(前端/图表/3D)

我在选题时最终选择了基于 Three.js 的校园 3D 导览系统,既有趣又能展示技术能力。

3. src/content/posts/post-2.md

---
title: 'Astro 入门指南'
date: 2024-04-01
description: '了解 Astro 岛屿架构和内容优先理念'
---

## 什么是岛屿架构?

Astro 默认在服务端渲染 HTML,只有需要交互的组件才会在客户端"水合"(hydrate)。

```astro
<!-- 这个组件在页面加载后立即水合 -->
<Counter client:load />

<!-- 这个组件仅当滚动到可见区域时才水合 -->
<Chart client:visible />

### 4. `src/components/PostCard.astro`
```astro
---
interface Props {
  title: string;
  date: string;
  description: string;
  slug: string;
}
const { title, date, description, slug } = Astro.props;
---

<article class="card">
  <h2><a href={`/posts/${slug}`}>{title}</a></h2>
  <time>{date}</time>
  <p>{description}</p>
</article>

<style>
  .card {
    background: white;
    padding: 20px;
    border-radius: 12px;
    margin-bottom: 16px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.08);
    transition: transform 0.2s;
  }
  .card:hover { transform: translateY(-2px); }
  .card h2 { margin: 0 0 8px; }
  .card h2 a { color: #1e293b; text-decoration: none; }
  .card time { color: #94a3b8; font-size: 0.9em; }
  .card p { color: #475569; margin-top: 8px; }
</style>

5. src/pages/index.astro

---
import PostCard from '../components/PostCard.astro';

// 模拟从 Content Collections 获取(实际用 getCollection)
const posts = [
  { slug: 'post-1', data: { title: '毕设选题心得', date: '2024-03-15', description: '如何选择一个合适的毕业设计题目' } },
  { slug: 'post-2', data: { title: 'Astro 入门指南', date: '2024-04-01', description: '了解 Astro 岛屿架构和内容优先理念' } },
];
---

<html lang="zh">
  <head>
    <meta charset="utf-8" />
    <title>我的技术博客</title>
  </head>
  <body>
    <header>
      <h1>🚀 我的技术博客</h1>
      <p class="subtitle">记录毕设开发的心得与收获</p>
    </header>

    <main>
      {
        posts.map((post) => (
          <PostCard
            title={post.data.title}
            date={post.data.date}
            description={post.data.description}
            slug={post.slug}
          />
        ))
      }
    </main>

    <footer>
      <p>Built with Astro · 零 JS 博客</p>
    </footer>
  </body>
</html>

<style is:global>
  body {
    font-family: system-ui;
    background: #f8fafc;
    max-width: 680px;
    margin: 0 auto;
    padding: 20px;
  }
  header {
    text-align: center;
    padding: 40px 0;
  }
  header h1 { margin: 0; font-size: 2.2em; }
  .subtitle { color: #64748b; }
  footer {
    text-align: center;
    color: #94a3b8;
    padding: 30px 0;
    font-size: 0.9em;
  }
</style>

运行步骤

npm create astro@latest astro-blog -- --template minimal
cd astro-blog
npm run dev

预期输出

  • 博客首页展示 2 篇文章卡片
  • 页面完全静态 HTML(查看源码无 JS!)
  • 卡片 hover 有上浮动画
  • 这是零 JS 页面,加载速度极快

Astro Hello World — 极简内容站点

目标

创建 Astro 内容站点,展示 .astro 组件、Markdown 渲染、以及多框架组件共存。

完整代码

首页 src/pages/index.astro

---
// 前置脚本(server-side,不会发送到浏览器)
const title = "🚀 我的 Astro 站点";
const description = "零 JavaScript,极致性能";
---

<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{title}</title>
  </head>
  <body>
    <main>
      <h1>{title}</h1>
      <p class="tagline">{description}</p>

      <!-- 组件 -->
      <Counter client:load />
      <RecentPosts />
    </main>
  </body>
</html>

<style>
  body {
    font-family: -apple-system, 'Microsoft YaHei', sans-serif;
    max-width: 640px;
    margin: 60px auto;
    padding: 0 16px;
    background: #fafafa;
  }
  h1 { font-size: 2.5rem; }
  .tagline { color: #666; font-size: 1.2rem; }
</style>

计数器组件 src/components/Counter.astro

---
// 纯 Astro 组件:无 JS 发送到客户端
let count = 0;
---

<div class="counter">
  <p>计数:<strong>{count}</strong></p>
  <p class="note">(纯服务端渲染,此处无法交互,需要 client 指令)</p>
</div>

<style>
  .counter {
    padding: 16px; border-radius: 12px;
    background: #f0f0ff; margin: 16px 0;
  }
  .note { font-size: 0.8rem; color: #999; }
</style>

带交互的 React 计数器 src/components/ReactCounter.jsx

import { useState } from "react";

export default function ReactCounter() {
  const [count, setCount] = useState(0);

  return (
    <div style={{ padding: 16, background: "#e8f5e9", borderRadius: 12, margin: "16px 0" }}>
      <p>React 计数器:<strong>{count}</strong></p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <p style={{ fontSize: "0.8rem", color: "#666" }}>
        此组件由 React 水合,具有客户端交互
      </p>
    </div>
  );
}

.astro 中使用:

---
import ReactCounter from "../components/ReactCounter";
---

<ReactCounter client:load />  <!-- client:load 使其在客户端水合 -->

博客文章 Markdown src/pages/posts/hello.md

---
title: "我的第一篇文章"
date: 2024-01-20
layout: ../../layouts/BlogPost.astro
---

# 你好,Astro!

Astro 的**内容集合**功能让 Markdown 管理变得极其优雅。

- ⚡ 默认零 JS
- 🏝️ 按需水合(Islands Architecture)
- 🎨 支持 React/Vue/Svelte/Preact/Solid 组件

博客布局 src/layouts/BlogPost.astro

---
const { frontmatter } = Astro.props;
---

<html lang="zh-CN">
  <head>
    <title>{frontmatter.title}</title>
  </head>
  <body>
    <main>
      <a href="/">← 返回</a>
      <h1>{frontmatter.title}</h1>
      <time>{frontmatter.date}</time>
      <article>
        <!-- slot 是 Markdown 内容 -->
        <slot />
      </article>
    </main>
  </body>
</html>

配置文件 astro.config.mjs

import { defineConfig } from "astro/config";
import react from "@astrojs/react";

export default defineConfig({
  integrations: [react()],
});

运行步骤

npm run dev        # 开发模式 http://localhost:4321
npm run build      # 构建(默认零 JS 输出!)
npm run preview    # 预览构建结果

预期输出

  • 首页渲染 HTML,查看源代码可见完整内容
  • React 计数器在客户端水合后可交互
  • 默认情况下页面 JS 体积接近 0KB

教程

Astro 入门教程:群岛架构与内容驱动

一、Astro 是什么?

Astro 是专注于内容驱动网站的现代静态站点生成器。它的核心理念是"Islands Architecture(群岛架构)"——页面默认是纯静态 HTML(零 JavaScript),仅在需要交互的地方引入"岛屿"(React/Vue/Svelte 组件水合到页面中)。

为什么这很重要?

传统 SPA 框架下,一个博客首页可能加载 200KB+ 的 JavaScript,而 Astro 的同一页面可能仅 0KB。对于文档站、营销站、电商产品页等以内容为主的场景,这是巨大的性能优势。

二、Islands Architecture 详解

┌─────────────────────────────────────┐
│           纯 HTML 海洋               │
│  (导航栏、标题、正文、页脚)            │
│                                     │
│  ┌─────────┐  ┌──────────────┐      │
│  │ React   │  │   Svelte      │      │
│  │ 轮播图   │  │  实时搜索      │      │
│  └─────────┘  └──────────────┘      │
│     ↑ 水合岛屿      ↑ 另一个岛屿      │
└─────────────────────────────────────┘

client 指令控制水合时机

指令 水合时机 适用场景
client:load 页面加载后立即 关键交互(导航菜单)
client:idle 浏览器空闲时 低优先级组件
client:visible 进入视口时 懒加载组件(评论区)
client:media 匹配媒体查询时 响应式组件
client:only 仅客户端渲染 依赖浏览器 API 的组件

三、内容集合(Content Collections)

Astro 2.0+ 的核心特性,为 Markdown/MDX 提供类型安全:

// src/content/config.ts
import { defineCollection, z } from "astro:content";

const blogCollection = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    date: z.date(),
    description: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog: blogCollection };

使用:

---
import { getCollection } from "astro:content";

const posts = await getCollection("blog", ({ data }) => !data.draft);
const sortedPosts = posts.sort(
  (a, b) => b.data.date.getTime() - a.data.date.getTime()
);
---

<ul>
  {sortedPosts.map((post) => (
    <li>
      <a href={`/blog/${post.slug}`}>{post.data.title}</a>
      <time>{post.data.date.toLocaleDateString()}</time>
    </li>
  ))}
</ul>

四、路由系统

src/pages/
├── index.astro          → /
├── about.astro          → /about
├── blog/
│   ├── index.astro      → /blog
│   └── [slug].astro     → /blog/:slug(动态路由)
├── rss.xml.js           → /rss.xml(API 端点)
└── 404.astro            → 自定义 404

动态路由 + SSG

---
// 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();
---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

五、视图过渡(View Transitions)

Astro 3.0+ 内置 SPA 般的页面过渡:

---
import { ViewTransitions } from "astro:transitions";
---

<html>
  <head>
    <ViewTransitions />
    <!-- 页面切换时自动应用原生 View Transition API -->
  </head>
  <body>
    <slot />
  </body>
</html>

六、部署

npm run build  # 输出到 dist/

# 支持所有主流平台:
# - Vercel:自动检测 Astro 框架
# - Netlify:用 @astrojs/netlify 适配器(SSR 模式)
# - Cloudflare Pages:用 @astrojs/cloudflare 适配器
# - 静态托管(GitHub Pages/S3 等):默认静态模式

七、思考题

  1. "零 JS" 真的意味着完全不能用 JavaScript 吗?什么情况需要 JS?
  2. Islands Architecture 和 Micro Frontends 有何异同?
  3. 如果一个页面有 20 个交互组件,Islands Architecture 还会有性能优势吗?

Astro 内容站点搭建入门

背景

Astro 是专为内容型网站设计的框架。如果你的毕设涉及博客、文档站、作品展示、公司官网等内容为主的项目,Astro 的"默认零 JS"理念能让你获得近乎完美的 Lighthouse 分数。同时它支持混合 Vue/React/Svelte 组件——这在答辩中是非常独特的卖点。


核心概念

岛屿架构

Astro 把页面想象成海洋,交互组件是岛屿:

┌────────────────────────────┐
│  纯 HTML (海洋)            │
│                            │
│  ┌──────┐    ┌──────────┐  │
│  │ Vue  │    │  React   │  │
│  │ 岛屿 │    │  岛屿    │  │
│  └──────┘    └──────────┘  │
│                            │
│  不需要 JS 的内容完全不加载 JS │
└────────────────────────────┘

.astro 单文件组件

---
// 组件脚本(服务端执行,不会发到浏览器)
const data = await fetch('https://api.example.com/posts');
const posts = await data.json();
---

<!-- HTML 模板 -->
<h1>博客文章</h1>
{posts.map(post => <PostCard {...post} />)}

分步操作

第一步:创建项目

npm create astro@latest
# 选择 Empty 模板 + TypeScript + 安装依赖
cd my-site
npm run dev

第二步:理解路由

src/pages/
├── index.astro          → /
├── about.astro          → /about
├── blog/
│   ├── index.astro      → /blog
│   └── [slug].astro     → /blog/post-1
└── 404.astro            → 404 页面

第三步:添加框架

npx astro add react

然后就可以在 .astro 文件中使用 React 组件:

---
import Counter from '../components/Counter.jsx';
---

<Counter client:load />  <!-- client:load 表示在客户端水合 -->

第四步:Content Collections

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blogCollection = defineCollection({
  schema: z.object({
    title: z.string(),
    date: z.date(),
    description: z.string(),
  }),
});

export const collections = { blog: blogCollection };

毕设推荐场景

作品展示站

展示毕设项目截图、技术栈、GitHub 链接。纯静态,加载快。

技术博客

记录毕设开发过程,Markdown 写作,自动生成 RSS。

多框架实验

一个页面同时用 Vue 表单 + React 图表 + Svelte 动画——展示技术广度。


思考题

  1. Astro 的岛屿架构与传统 SPA 的水合(Hydration)有什么区别?
  2. client:load vs client:visible 分别适用于什么场景?
  3. Content Collections 相比直接 import Markdown 有什么优势?

小结

Astro 是内容网站的终极方案。毕设中的作品展示、技术博客、文档页用它搭建,可以获得满分 Lighthouse 评分 + 独特的岛屿架构谈资。

参考资料

  1. [1] Astro Team. Astro Documentation. 2024. https://docs.astro.build
  2. [2] Jason Miller. Islands Architecture. 2020.
  3. [3] Fred K. Schott. Astro: A New Approach to Frontend. 2022. https://astro.build/blog
  4. [4] Astro 团队. Astro 官方文档. 2024. https://docs.astro.build/
  5. [5] Astro 团队. Astro 教程:构建博客. 2024. https://docs.astro.build/en/tutorial/0-introduction/
  6. [6] Fred K. Schott. Islands Architecture 详解. 2022. https://docs.astro.build/en/concepts/islands/