纸上山河 记录技术、思考与生活

为什么个人博客应该做成静态

一个十年不管还能活着的工程决策

约 2 分钟 making

个人博客是所有技术项目里最容易被过度设计的一个。需求听起来很重——写作系统、内容管理、用户体系、评论、SEO——但真正把问题拆开,会发现里面绝大部分是空的。

先问一个能定生死的问题

访客访问的时候,你的服务器上需不需要有东西在跑?

如果答案是「不需要」,那所有方案都收敛到同一条路:文章在本地写成 Markdown,构建成静态 HTML,扔给 Nginx。访问时零数据库、零 Node 进程、零攻击面。

这一问的价值在于它能排除掉大量方案。比如 Next.js——它很强,但每篇文章都要带上 React runtime(压缩后约 85KB),而你一个交互组件都不需要。

代价不在流量,在运维

代价不只是流量,还有云服务器上那个需要长期打补丁的 Node 进程。你要管 pm2 守护、进程重启、依赖漏洞扫描、版本升级。这些工作和「写文章」没有任何关系,却要持续消耗你的注意力。

内容应该住在 Git 里

一旦你引入数据库,内容就进了一个需要备份、需要升级、需要防注入的运维循环。而内容留在 Git 仓库里,本身就是最好的备份——GitHub 就是你的数据库,还是带版本历史、能回滚、能协作的那个。

这个判断可以推广:任何时候你发现要给一个简单功能引入一套需要长期维护的基础设施,都值得停下来重新想一遍。

实现要点

构建期做校验,把错误挡在上线之前:

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(),
    // 用 coerce.date() 而非 string,日期写错当场报错
    publishDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

草稿用 draft: true 标记,构建时自动排除:

const posts = await getCollection('blog', ({ data }) => !data.draft);

什么时候该重新考虑

有三种情况会让你想收回这个决定:

  1. 需要多用户同时写作(那就该有后台)
  2. 需要评论和账号体系(那就该有数据库)
  3. 文章内容需要在发布后动态修改(那就该用 CMS)

如果这三样都不需要,静态就是最优解。

写下来:技术选型的判断标准往往不是「哪个框架更先进」,而是「它的能力有没有被我用上」。用不上的能力不只是浪费,它还在持续向你收费。