技术文章

用 Astro 搭一个不吃灰的个人站

从零到上线的完整记录:为什么选静态站、内容怎么组织、以及在国内访问速度上做了哪些具体权衡。

  • Astro
  • 前端
  • 建站

这篇是本站自己的搭建记录。写下来一是给自己留个档,二是如果你也想搭一个,可以少走几步弯路。

为什么是静态站

个人博客的读写比极度不平衡 —— 一篇文章写一次,被读几百上千次。既然如此,没有理由让每次访问都去跑一遍数据库查询。静态站在构建时就把所有页面渲染成 HTML,访问时只是下发文件。

代价是每次改内容都要重新构建。但对博客来说,这个代价由 CI 承担,我本人感觉不到。

内容怎么组织

文章是仓库里的 Markdown 文件,frontmatter 里放元信息:

---
title: 文章标题
description: 一句话摘要
pubDate: 2026-08-16
tags: ['Astro', '前端']
draft: false
---

关键是 draft 字段。它在开发时可见、构建时排除:

const items = await getCollection(section, ({ data }) =>
  import.meta.env.DEV ? true : data.draft !== true
);

这样草稿能在本地预览完整效果,又不会因为一次误推送就上线。写到一半的东西不该被人看到。

图片是最容易翻车的地方

摄影栏目的图片如果原样下发,一张手机拍的照片就是 4-6 MB。构建期处理是唯一合理的做法:

处理效果
转 WebP / AVIF体积降到原来的 30% 左右
生成多尺寸 srcset手机不必下载 1600px 的图
构建期完成访问时零转换开销

Astro 的 image() 让图片进入 schema 校验,路径写错在构建时就报错,而不是等上线后看到裂图。

国内访问的取舍

这是花时间最多的部分。托管在境外,跨境延迟无法消除,但可以减少请求次数

  • 字体不引 CDN。中文字体动辄十几 MB,不可能自托管,所以中文走系统字体栈,西文用构建期打包的 Fraunces。省掉一次跨境请求,也避免了字体加载失败导致的排版跳动。
  • 数学公式用 KaTeX 但自托管 CSS。构建产物里自带,不依赖第三方。
  • 零运行时框架。整站没有客户端 JS 框架,只有一小段切换深浅色的原生脚本。

有个数学上的直观理由:假设单次跨境往返 200ms,把 6 个外部资源请求降到 0,在无法并行的最坏情况下省下

Tsaved=6×200ms=1.2sT_{\text{saved}} = 6 \times 200\,\text{ms} = 1.2\,\text{s}

即便浏览器能并行,省掉的 DNS 解析和 TLS 握手也是实打实的。首屏时间里,请求数往往比字节数更致命

还没做的事

  • 全文搜索。文章少的时候浏览就够,超过五十篇再说。
  • 评论。还没想清楚要不要。
  • 图片 EXIF 展示。摄影栏目值得加,但不急。

先上线,再迭代。一直在本地打磨的站,永远上不了线。