温素纸
技术约 560 字 · 1 分钟

用 Astro 搭博客:零 JS 是怎么做到的

Astro 的孤岛架构和默认零 JS 输出,为什么特别适合内容型网站。

Astro 有个说法叫“默认零 JavaScript”。第一次听到会觉得是营销话术,看完它的工作方式才明白,这句话是字面意思。

传统框架的代价

用 React 写博客,哪怕页面上一个交互都没有,浏览器也要下载并解析 React 运行时。一个简单的文章页,JS 体积轻松上 200 KB。

这些 JS 做的事情,其实只是把服务端已经算好的东西,在浏览器里再算一遍。

孤岛架构

Astro 的思路是把页面看成一片静态的海洋,只把真正需要交互的部分做成“孤岛”:

---
// 这部分在构建时运行,不会进浏览器
import { getCollection } from 'astro:content';
const posts = await getCollection('posts');
---

<article>
  <h1>{posts[0].data.title}</h1>
  <!-- 只有这个组件会把 JS 带到浏览器 -->
  <LikeButton client:load />
</article>

client:load 这类指令才告诉 Astro“这块需要 hydrate”。没写指令的组件,构建完就变成了纯 HTML。

博客里绝大多数东西——标题、正文、列表、日期——都不需要 JS。所以最终产物就是 HTML + CSS。

内容集合

Markdown 的管理用 Content Collections,schema 用 zod 校验:

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

const posts = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
  schema: z.object({
    title: z.string(),
    date: z.coerce.date(),
    kind: z.enum(['tech', 'essay']),
    tags: z.array(z.string()).default([]),
  }),
});

export const collections = { posts };

写错了 frontmatter,构建时就会报错,而不是等到线上白屏。

真正需要 JS 的地方

这个站里只有三处用了 JS,加起来不到 60 行:

  1. 阅读进度条 —— 监听 scroll,改一个元素的宽度
  2. 主题切换 —— 读写 localStorage,切 data-theme
  3. 图片灯箱 —— 点开大图,Esc 关闭

这三件事都没有必要引入框架,原生写反而更短。

需要注意的

  • Shiki 明暗双主题要自己写 CSS 接住,Astro 只负责生成 --shiki-dark 这些变量
  • 中文字体别整套塞 webfont,用系统字体栈
  • 图片public/ 下就不会被 Astro 处理;放 src/assets 才会走优化管线

静态站点最大的好处不是快,是省心。没有数据库要备份,没有依赖会出安全漏洞,停电了也不会丢数据。

—— 于灯下2026 年 8 月 3 日

回响