Astro

返回 JavaScript 框架

Astro 是以内容为中心的 Web 框架,默认输出零 JS 的静态 HTML,通过”孤岛架构”(Islands Architecture)按需水合交互组件。支持集成 React、Vue、Svelte、Solid 等多种 UI 框架。


核心理念

传统 SPA:整页 JS bundle → 水合整个页面(大量 JS 下载)
Astro:全页静态 HTML + 孤岛(Islands)→ 仅水合交互部分

孤岛(Island):页面中独立的交互区域,每个孤岛按需加载自己的 JS,互不干扰。其余部分是纯 HTML,无运行时开销。


项目结构

my-project/
├── src/
│   ├── pages/        ← 路由(.astro / .md / .mdx / .html)
│   ├── components/   ← 组件(.astro / .svelte / .tsx 等)
│   ├── layouts/      ← 布局组件
│   ├── content/      ← 内容集合(Markdown/MDX)
│   ├── styles/       ← 全局样式
│   └── utils/        ← 工具函数
├── public/           ← 静态资源(原样复制到输出目录)
├── astro.config.mjs
└── package.json

.astro 文件语法

.astro 文件由 frontmatter(服务端 JS)和 模板(类 HTML)组成:

---
// frontmatter:仅在构建/服务端运行,不会发送到浏览器
import MyComponent from '../components/MyComponent.astro';
const title = 'Hello Astro';
const items = ['Apple', 'Banana'];
---
 
<!-- 模板:JSX 风格,但是 HTML -->
<html>
  <head><title>{title}</title></head>
  <body>
    <MyComponent />
    <ul>
      {items.map(item => <li>{item}</li>)}
    </ul>
  </body>
</html>

页面与路由

静态路由

src/pages/ 下的文件自动映射为路由:

src/pages/index.astro     → /
src/pages/about.astro     → /about
src/pages/blog/index.astro → /blog

动态路由

---
// src/pages/posts/[slug].astro
export function getStaticPaths() {
  return [
    { params: { slug: 'hello-world' } },
    { params: { slug: 'my-post' } },
  ];
}
const { slug } = Astro.params;
---
<h1>{slug}</h1>

SSR 动态路由

开启 output: 'server' 后无需 getStaticPaths,直接读 Astro.params

---
const { id } = Astro.params;
const data = await fetch(`/api/items/${id}`).then(r => r.json());
---

Layouts(布局)

布局是带 <slot /> 的特殊组件,用于复用页面壳:

---
// src/layouts/BaseLayout.astro
const { title } = Astro.props;
---
<!DOCTYPE html>
<html>
  <head><title>{title}</title></head>
  <body>
    <nav>导航</nav>
    <slot />      <!-- 子内容插入此处 -->
    <footer>页脚</footer>
  </body>
</html>

使用:

---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="我的页面">
  <h1>正文内容</h1>
</BaseLayout>

具名 slot

<!-- 布局中 -->
<slot name="head" />
<slot />
 
<!-- 页面中 -->
<BaseLayout>
  <link slot="head" rel="stylesheet" href="/extra.css" />
  <p>主内容</p>
</BaseLayout>

组件集成(Islands)

通过 client:* 指令控制水合时机:

指令时机
client:load页面加载立即水合
client:idle浏览器空闲时水合
client:visible组件进入视口时水合
client:media="(max-width: 768px)"媒体查询匹配时水合
client:only="svelte"仅客户端渲染,跳过 SSR
---
import Counter from '../components/Counter.svelte';
import Chart from '../components/Chart.tsx';
---
 
<!-- 服务端渲染静态 HTML,不含 JS -->
<Counter />
 
<!-- 页面加载后立即水合为可交互组件 -->
<Counter client:load />
 
<!-- 滚动到此处才加载 JS -->
<Chart client:visible />

数据获取

构建时(SSG)

---
// 构建时运行,结果静态化到 HTML
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
---
{posts.map(p => <article>{p.title}</article>)}

请求时(SSR)

---
// output: 'server' 模式下,每次请求时运行
const user = await getUser(Astro.cookies.get('session')?.value);
if (!user) return Astro.redirect('/login');
---
<p>欢迎,{user.name}</p>

API 路由

src/pages/api/ 下的 .ts 文件导出 GET/POST 等处理函数:

// src/pages/api/hello.ts
import type { APIRoute } from 'astro';
 
export const GET: APIRoute = ({ url }) => {
  const name = url.searchParams.get('name') ?? 'World';
  return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
    headers: { 'Content-Type': 'application/json' },
  });
};

内容集合(Content Collections)

src/content/ 下的 Markdown/MDX 文件进行类型安全的管理:

// src/content/config.ts
import { defineCollection, z } from 'astro:content';
 
const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    tags: z.array(z.string()).optional(),
  }),
});
 
export const collections = { blog };

查询:

---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
{posts.map(p => <a href={`/blog/${p.slug}`}>{p.data.title}</a>)}

集成(Integrations)

通过 astro.config.mjs 中的 integrations 字段添加:

import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';
 
export default defineConfig({
  integrations: [svelte(), react(), tailwind(), sitemap()],
});

常用官方集成:

集成作用
@astrojs/svelteSvelte 组件支持
@astrojs/reactReact 组件支持
@astrojs/vueVue 组件支持
@astrojs/tailwindTailwind CSS
@astrojs/sitemap自动生成 sitemap
@astrojs/mdxMDX 支持
@astrojs/image图片优化

中间件

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
 
export const onRequest = defineMiddleware(async (context, next) => {
  const { request, cookies, redirect } = context;
 
  // 鉴权示例
  if (request.url.includes('/admin')) {
    const token = cookies.get('token')?.value;
    if (!token) return redirect('/login');
  }
 
  return next();
});

渲染模式

模式配置适用场景
SSG(默认)output: 'static'博客、文档,部署到 CDN
SSRoutput: 'server'用户鉴权、实时数据、个性化内容
混合output: 'hybrid'大部分静态 + 少量动态页面

SSR 需要适配器:

import node from '@astrojs/node';
 
export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});

Markdown 处理

Astro 内置 Markdown 支持,可通过 remark/rehype 插件扩展:

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkMath, remarkGfm],
    rehypePlugins: [rehypeKatex, rehypeSlug],
    syntaxHighlight: 'shiki',
    shikiConfig: { theme: 'github-dark' },
  },
});

本项目架构(KumaBlog)

src/pages/      ← 薄壳页面,重交互页面渲染单个 Svelte 组件
src/components/ ← Svelte 组件(全页 UI:日记、项目、留言板等)
src/layouts/
  ├── Layout.astro          ← 基础 HTML 壳(head、主题初始化)
  └── MainGridLayout.astro  ← 带导航栏、Banner、TOC 的主网格布局
src/middleware.ts   ← 开发环境将 /api/* 代理到 Java 后端 :9000

数据来源:

  • Java 后端 API(文章、项目、留言板)→ 通过 /api/* 代理/直连
  • 静态 TS 数据文件(友链、fallback 项目列表)

部署:Astro 静态输出 + Nginx 反代 /api 到 Java 后端。