跳到正文
返回文章列表

用 Astro 搭建一个干净的静态站点

为什么我最终选择了 Astro,以及一个静态博客的项目结构应该长什么样。

用 Astro 搭建一个干净的静态站点

选技术栈这件事,我的原则一向是:让它尽可能简单,直到简单无法解决问题为止。

为什么是 Astro

在几个候选方案里纠结了一阵之后,我选了 Astro,原因有几个:

  • 默认零 JavaScript。页面是纯 HTML,只有需要交互的地方才加载脚本。
  • 内容即文件。文章就是 Markdown 文件,用 Git 管理,天然有版本历史。
  • 构建产物是静态的。可以直接扔到 GitHub Pages、Cloudflare Pages 或任何 CDN 上。
  • 岛屿架构。哪天需要一个交互组件,可以单独把它”激活”,而不影响其他部分。

项目结构

一个维护起来舒服的结构大概是这样:

.
├── src/
│   ├── components/      # 可复用组件
│   ├── content/
│   │   └── posts/       # ← 文章都放这里
│   ├── layouts/         # 页面骨架
│   ├── pages/           # 路由:文件路径 = URL
│   └── styles/          # 全局样式
├── public/              # 静态资源(图片、favicon)
└── astro.config.mjs

关键在于 src/pages/ 的约定:文件系统的路径就是 URL 路径。

比如 src/pages/about.astro 对应 /about,src/pages/posts/[...slug].astro 则是一个动态路由,可以匹配 /posts/任意文章名。

动态路由是怎么工作的

这是 Astro 里最需要理解的一个概念。动态路由页面需要导出一个 getStaticPaths 函数,告诉构建器”有哪些页面需要生成”:

export async function getStaticPaths() {
  const posts = await getCollection('posts');

  return posts.map((post) => ({
    params: { slug: post.id },
    props: { post },
  }));
}

构建时,Astro 会为每篇文章调用一次这个页面模板,生成一个独立的 HTML 文件。部署后没有运行时,只有文件。

这就是静态站点快的根本原因:请求进来时,服务器不需要”计算”任何东西,只需要把已经存在的文件递出去。

关于样式的取舍

我刻意没有引入 UI 框架,而是用原生 CSS + 自定义属性(CSS Variables)来做设计系统:

:root {
  --accent: #4f46e5;
  --radius: 14px;
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

.card {
  border-radius: var(--radius);
  transition: transform 0.45s var(--ease);
}

好处是:

  • 没有构建期的样式处理负担
  • 深色模式只需要覆盖一层变量
  • 想调整视觉,改几个变量就够了

部署

如果是 GitHub Pages,加一个 workflow 就够了:

- uses: actions/checkout@v4
- uses: withastro/action@v3

提交之后自动构建、自动发布。整个流程不需要我再操心。

小结

技术选型的本质是约束管理。每引入一个依赖,就多一份将来要维护的东西。

Astro 让我用很小的代价获得了一个又快又干净的站点。对个人博客来说,这已经足够了。