格物致知

为什么这个博客可以一行 JavaScript 都不要

约 1 分钟

搭这个博客之前我列了一张清单,问自己:访问者打开一篇文章,到底需要浏览器做什么?

答案是什么都不用做。他需要的是文字、排版、一张图片。这些东西在服务器上是死的,传过去就能看。

运行时复杂度与构建时复杂度

前端框架的流行让人产生一种错觉:复杂度是免费的。其实不是,它只是被推迟了——从构建时推迟到了运行时,从你的机器推迟到了每一个访客的手机上。

同一份复杂度,放在两边代价完全不同:

构建时运行时
谁来承担你的电脑,一次每个访客,每次
出错的后果构建失败,你立刻知道白屏,访客直接走
能否回滚改一行重跑改一行,等下次访问

一篇文章的渲染逻辑,一年可能只变两次;但它每次被访问都要重新执行一遍。把不变的活放在构建时做,是稳赚的。

内容就是文件

这个站里的每一篇文章,都是一个 Markdown 文件:

content/notes/2026-09-18-tui-shitou-de-na-ge-zao-chen.md

文件开头有一段被称为 front-matter 的说明:

---
title: 推石头的那个早晨
date: 2026-09-18
tags: [加缪, 读书笔记]
---

构建的时候,Astro 会扫描整个目录,用一份 schema 校验每个文件的字段,然后把它们变成路由和列表页:

const postCollection = (dir) =>
  defineCollection({
    loader: glob({ base: `./content/${dir}`, pattern: '**/*.md' }),
    schema: z.object({
      title: z.string(),
      date: z.coerce.date(),
      tags: z.array(z.string()).default([]),
    }),
  });

好处是:字段名写错,构建直接失败,而不是等到页面上少显示一个日期你才发现。

代价是什么

天下没有白拿的东西。零 JavaScript 换来的代价是:

  • 没有客户端路由,每次点击都是整页跳转。但静态 HTML 加 HTTP 缓存之后,这个跳转通常比 SPA 的首次渲染更快。
  • 没法做需要登录的交互。留言板因此必须外挂一个独立的后端,不能和页面打包在一起。
  • 搜索要额外处理。我用构建期生成索引的 Pagefind,仍然是纯前端查询,不需要服务器。

一个判断标准

我现在的判断标准很粗暴:这个功能,如果访客关掉 JavaScript 会怎样?

导航、阅读、切换深浅色、看代码——全都应该照常。留言板除外,它是唯一一个真的需要脚本的功能,而且它挂了也不影响读文章。

一个博客最核心的能力是让人把字读完。其他都是附加项。