前端工程

用Astro搭建静态博客:从内容集合开始到部署

把博客做成纯静态站点,内容用Markdown维护,构建产物丢到任意静态托管。记录选型理由、内容集合写法与上线前的检查清单。

个人技术博客最容易踩的坑不是选不出框架,而是把内容管理做重了:数据库、后台、登录、编辑器插件,最后文章没写几篇,维护后台倒成了负担。

为什么是静态生成

技术博客的读写比极端失衡:一个人写,很多人读,且内容几乎不变。这种场景下最合理的架构是构建时把数据全部算好,运行时只返回HTML文件。

带来的好处很直接:

  • 没有应用服务器和数据库连接,托管成本几乎为零,随便一台Nginx或对象存储都能放;
  • 首屏不需要等待接口返回,TTFB和LCP稳定,对SEO友好;
  • 内容就是仓库里的.md文件,备份、回滚、版本对比都用Git,不需要额外的内容管理后台。

代价是每次更新都要重新构建,以及动态功能(评论、搜索)得另想办法。前者对个人博客完全无所谓,后者可以用客户端索引和独立的小API解决。

内容集合定义元数据

Astro的内容层用glob()把目录里的Markdown加载成集合,再用Zod约束frontmatter。字段写错会在构建阶段直接报错,比上线后才发现日期格式不对强得多。

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const posts = defineCollection({
  loader: glob({ base: './src/content/posts', pattern: '**/*.md' }),
  schema: z.object({
    title: z.string().min(1),
    description: z.string().min(1),
    pubDate: z.coerce.date(),
    category: z.string().default('未分类'),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { posts };

一个容易忽略的细节:YAML里的pubDate: 2026-11-02会被解析成UTC午夜。格式化时一律用getUTCFullYear()这类UTC取值方法,否则西半球时区的构建机会把日期显示成前一天。

页面由数据反推

分类页、标签页、归档页、站点地图、RSS和搜索索引,全部由同一份文章列表在构建时生成,不需要手工维护任何清单。新增一篇文章后,这些页面自动同步。

const posts = await getCollection('posts', ({ data }) => !data.draft);
const sorted = posts.sort((a, b) =>
  b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);

getCollection()返回顺序是不确定的,任何依赖顺序的地方都必须自己排序,这一点文档里也写了,但很容易漏。

上线前检查清单

  1. 配置站点绝对地址,它会决定canonical、OG标签、sitemap和RSS里的链接;
  2. 所有静态页面统一以斜杠结尾,Nginx里配好try_files或$uri/index.html;
  3. 检查404页是否真的返回404状态码,而不是软404;
  4. 把robots.txt里的sitemap地址改成正式域名;
  5. 用构建产物本地起一次服务验证,别只信开发服务器。

静态站点真正的优势不是快,而是几乎没有什么会坏。这一点在两年后回来改一篇旧文章时,会显得格外值钱。

评论 0

评论保存在本站服务器,无需登录。带「作者」标识的是站主回复,请友善交流。

    评论内容至少2个字、最多1000字,按纯文本展示,不支持HTML。