学习
用 Astro 搭建一个内容优先的博客
搭一个博客时,方案大致分两类:一边是 Next.js / Nuxt 这类全功能框架,能力强但默认带一整套运行时;一边是 Hugo / Zola 这类纯静态生成器,够快但模板系统受限。Astro 的定位正好落在中间——它以内容为中心,默认输出零 JavaScript 的静态页面,同时保留了组件化和 TypeScript 的完整体验。
下面按「上手 → 核心特性 → 部署」的顺序,梳理一遍用 Astro 做内容型站点的完整路径,并附上相关文档链接,方便你边看边查。
快速开始
Astro 需要 Node.js 18 及以上版本。用官方脚手架初始化一个项目:
npm create astro@latest
脚手架会问你选模板(可以选 blog 模板,也可以从空项目开始)、是否启用 TypeScript 等。装好依赖后:
npm run dev # 本地开发,默认 http://localhost:4321
npm run build # 构建,产物在 dist/
npm run preview # 本地预览构建产物
一个典型的目录结构是这样的:
src/
content/ # Markdown/MDX 内容
content.config.ts # 内容集合的 schema 定义
layouts/ # 页面布局组件
pages/ # 路由(文件即路由)
components/ # 可复用组件
public/ # 静态资源,原样拷贝到 dist/
astro.config.mjs # 全局配置
src/pages/ 里的文件直接映射成路由——src/pages/about.astro 就是 /about。这套基于文件系统的路由不需要额外配置。参考:Project Structure。
内容集合:把 Markdown 当成有类型的数据
博客的核心是一堆 Markdown 文件,麻烦之处在于 frontmatter 很容易写错——日期格式不对、漏了字段、tag 写成字符串而不是数组,这些问题往往到构建甚至上线后才暴露。
Astro 的**内容集合(Content Collections)**用 Zod schema 把这件事前置到了构建期。你在 src/content.config.ts 里定义一个集合:
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
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 };
这段代码里每个约束都省去了后续大量的防御性判断:
z.coerce.date()把 frontmatter 里的2026-07-01直接转成Date对象,页面里可以放心调用.toLocaleDateString(),不必自己解析字符串。z.string()这类必填约束意味着写错或漏写字段时,astro build会直接失败并指出是哪个文件,而不是安静地渲染出一个残缺页面。.default(...)和.optional()明确区分「有默认值的字段」和「可以缺省的字段」,模板里就不用到处写frontmatter.tags ?? []。
Astro 5 引入的 glob loader 把「文件在哪、怎么读」和「数据长什么样」解耦了:base 指向目录,pattern 决定收录范围。往目录里丢一个 .md 就自动收录,不需要维护任何索引文件。
页面里消费这些数据是类型安全的:
---
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
data 的形状完全来自 schema,编辑器里能自动补全,拼错字段名会直接报类型错误。上面这行还顺手过滤掉了 draft: true 的草稿——本地开发能预览,构建产物里则不会包含。文档:Content Collections。
岛屿架构:默认零 JavaScript
Astro 最有辨识度的设计是岛屿架构(Islands Architecture):页面默认全部渲染成静态 HTML,浏览器拿到的就是纯 HTML + CSS,不附带任何框架运行时。只有当你显式给某个组件加上 client:* 指令时,那一小块才会「激活」(hydrate)成可交互的岛屿。
---
import Counter from '../components/Counter.jsx';
---
<!-- 静态渲染,零 JS -->
<Counter />
<!-- 页面加载后激活成可交互岛屿 -->
<Counter client:load />
对博客这种以阅读为主的站点,这个默认值几乎是免费的性能:大部分页面根本不需要客户端 JavaScript,首屏就是浏览器最擅长渲染的静态文档。需要交互的地方才单独作为岛屿加载,互不牵连。这和「先引入整个 React 运行时,再想办法优化掉」的路径正好相反——Astro 是从零开始按需添加。
值得一提的是 Astro 对框架不设限:通过官方 integration,你可以在同一个项目里混用 React、Vue、Svelte、Solid 等组件。文档:Islands。
Markdown 管线:可插拔的 remark / rehype
内容型站点迟早会遇到「Markdown 不够用」的时刻——要写数学公式、要高亮代码、要做提示框。Astro 直接复用了 unified 生态的 remark / rehype 插件体系,在 astro.config.mjs 里配置:
import { defineConfig } from 'astro/config';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
export default defineConfig({
markdown: {
remarkPlugins: [remarkMath], // 处理 Markdown 语法树
rehypePlugins: [rehypeKatex], // 处理转换后的 HTML 树
shikiConfig: { theme: 'github-dark', wrap: true },
},
});
分工是这样的:remark 处理 Markdown 抽象语法树(比如 remark-math 识别 $...$ 公式语法),rehype 处理转换后的 HTML 树(rehype-katex 把公式渲染成 KaTeX)。代码高亮交给内置的 Shiki——它在构建期就把高亮算好,输出带内联样式的 HTML,运行时不需要 JavaScript。
关键点是:这些处理全部发生在构建期。读者拿到的是已经渲染好的公式、已经高亮好的代码,浏览器不做二次计算。想要更强的组件能力,还可以启用 MDX,在 Markdown 里直接写组件。文档:Markdown。
站内搜索与 RSS
搜索通常是静态站点最尴尬的一环。一个零后端的方案是 Pagefind:它在构建之后扫描生成好的 HTML,产出一份分片的静态索引,搜索时按需加载对应分片,索引本身就是随页面一起部署的静态文件。
RSS 也有官方支持——装上 @astrojs/rss 即可从内容集合生成订阅源,文档见 RSS。
部署:纯静态产物,随处可放
Astro 默认构建出一个纯静态的 dist/ 目录,没有 Node 服务要常驻,没有运行时要守护——这让部署变得非常简单。常见几种方式:
- 平台托管(最省事):Vercel、Netlify、Cloudflare Pages 都能连 Git 仓库自动构建部署,推送即上线,免费额度对个人博客绰绰有余。
- GitHub Pages:Astro 官方提供了现成的 Actions 工作流,配置好仓库和
site/base即可。见 Deploy to GitHub Pages。 - 自己的服务器:CI 里跑一次
astro build,把dist/同步(rsync / scp)到服务器,交给 Nginx 之类的静态服务器托管即可。因为产物是纯静态的,跑在生产环境的东西跟你本地astro preview看到的完全一致。
各平台的具体步骤可以查 Deployment Guides 这一节,几乎主流平台都有对应文档。
小贴士:部署前记得在
astro.config.mjs里把site设成你的正式域名——RSS、canonical 链接、sitemap 都依赖它生成正确的绝对地址。
小结与延伸
一句话概括为什么选 Astro:它把「内容是有类型的数据」和「页面默认是静态的」这两件事做成了默认值,而不是需要你费力争取的优化项。对一个以写作为核心、又不想在前端工程上过度投入的博客来说,这套默认值省掉的正是最容易出错、最消耗精力的部分。
想继续深入,几个官方入口:
- 文档首页:https://docs.astro.build/
- 官方教程(从零做一个博客):https://docs.astro.build/en/tutorial/0-introduction/
- 主题与模板市场:https://astro.build/themes/
- 集成插件目录:https://astro.build/integrations/