blog-demo · 归档与说明

内容架构与工程化设计

站点从框架原型转向真实内容档案时的早期决策记录。

状态
历史记录
最后核对
源码修订
e0d3baa2

内容架构与工程化设计

  • 日期:2026-08-27
  • 状态:历史设计记录,保留当时的决策背景;实现与字段以内容模型与维护指南和 src/content.config.ts 为准。
  • 适用范围:迩迩的小站(Astro + GitHub Pages)从「框架原型」转向「真实内容站」

1. 背景

站点框架已经搭完:布局统一、暗色模式、跨页转场、CI 自动部署、19 项浏览器冒烟测试、 构建期数据抓取带三级降级。但七个兴趣页装的都是一次性交互 demo,没有真实内容, 也没有把内容装进去的顺手办法。

这份文档定的是:内容怎么组织、页面怎么长、用什么工具往里写。

2. 目标与非目标

目标

  • 三种内容类型覆盖全部兴趣板块,代码写一次、七个页面共用
  • 有一个图形界面能写图文,不用再记「哪个文件放哪个目录」
  • 站点保持零成本、零运维

非目标(明确不做,避免反复)

  • 不引入后端服务和数据库。 见 §3 决策一
  • 不做评论、不做访问统计、不做多人协作
  • 不做移动端发布(编辑器只在本机跑)

3. 关键决策记录

记下来是为了将来不再重新讨论一遍。

决策一:不上后端和数据库,git 就是数据库

曾考虑「前后端数据库解耦」以求工程上更专业,否决。

对内容站而言这是架构错配:

  • git 已经提供了想要的一切:版本记录、变更历史、逐行 diff、任意回滚、异地备份,且免费
  • 引入 PostgreSQL + API 服务器会带来服务器账单、运维负担、备份责任、可用性风险, 换来的新功能是零
  • 真正需要后端的是评论、多人协作、动态查询、实时更新——本站一个都不需要

将来什么情况下该重新考虑: 想要评论系统、想跨设备实时编辑、 或内容量大到构建时间不可接受(数千篇量级)。在此之前不要动这个决定。

决策二:图片直接进 git 仓库

发布节奏是「随手记,一周几张」,一年百来张。构建期压缩转 WebP 后, 一年增量约几十 MB,几年内碰不到 GitHub 的任何限制。

但图片路径要抽一层(统一走一个 helper,不在内容里写死 URL), 将来真要迁到对象存储只改一个配置,不动内容、不动页面。

决策三:摄影与旅行合并为 /moments/,提到顶层导航

两者重复:拍照的动机是记录日常,不是作品集,按「摄影/旅行」分类是给自己找活干。 合并后按时间线排,并提到顶层——它会是更新最频繁的内容,埋在兴趣二级页里太深。

旧链接 /interests/photography/、/interests/travel/ 合并后失效, 确认不做重定向(从未对外分享过)。

决策四:moment 与 essay 的界限

  • moment:不超过一段话。超过就该写成 essay
  • essay:成篇的文章

界限必须清楚,否则两边都想发、两边都懒得发。

决策五:交互 demo 降为页内彩蛋,股票页除外

  • 股票页(大盘复盘 + 自选股)本身就是内容——真数据、每天变,保持现状不动
  • 钢琴、代码沙箱、菜谱计算器等纯玩具:收到对应兴趣页底部的小区块,不再挡在内容前面

4. 内容模型

三个 content collection 覆盖全部需求。

src/content/
  essays/     长文:音乐故事、读书感触、现有博客三合一
  items/      收藏条目:歌、书(将来加影视也放这里)
  moments/    图文动态:摄影 + 旅行合并后的时间线

4.1 essays(长文)

字段 类型 说明
title string
description string 列表与分享卡片用
pubDate date
topic enum blog / music / reading / cooking / coding
tags string[]
draft boolean 默认 false

长文这边不存关联字段。关联只在 item 侧填一次(items.essay), 长文页需要时反查即可。两边都存必然有一天不同步。

现有 src/content/posts/ 的三篇文章迁移到这里,topic: 'blog'。

4.2 items(收藏条目)

字段 类型 说明
kind enum song / book
title string
creator string 歌手 / 作者
cover image? 封面
date date 歌:听到的时间;书:读完的时间,在读的书用开始时间
rating number? 1–5
blurb string 一句话短评
status enum? want / doing / done,书架分区用;歌可留空
essay string? 关联长文的 slug

4.3 moments(图文动态)

字段 类型 说明
date datetime 优先取 EXIF 拍摄时间
place string? 地点
photos array { src, alt, exif? }
text string 一段话,不超过一段

exif 子字段(编辑器自动填,见 §7):camera / lens / focal / aperture / shutter / iso。

4.4 双向关联

items.essay ←→ essays.relatedItem 构成双向链接:

  • 书架点一本书 → 跳到读后感
  • 读后感页顶部显示这本书的封面与评分

这样「歌单」和「故事会」不是两块割裂区域,而是同一批数据的两种视图, 不会出现「歌单里有的歌,故事会里找不到」。

一致性由构建期校验保证:essay 指向不存在的 slug 时构建报错,不留到线上。


5. 路由与页面

路由 内容 备注
/ 首页 增加最新 moments 入口
/blog/ essays(全部 topic) 现有
/moments/ 图文时间线 新增,进主导航
/interests/music/ 歌单(items kind=song)+ 故事入口 钢琴降为彩蛋
/interests/reading/ 书架(items kind=book,按 status 分区)+ 读后感入口
/interests/stocks/ 大盘复盘 + 自选股 不动
/interests/cooking/ 等 内容 + 底部 demo 彩蛋
/year/2026/ 年度回顾 阶段三,形态待定,见 §9

排版沿用既定规则:索引页 = 宽双栏,阅读页 = 窄单栏。


6. 阶段划分

每个阶段单独走一遍设计 → 计划 → 实施,不一口气糊完。

阶段一:内容模型 + 页面组件

定义三种类型的 schema,把七个手写 demo 页重构成按类型渲染,迁移现有文章。

完成标志:手写 Markdown 就能发内容。虽然还不舒服,但站已经能装东西了—— 这一步有独立价值,不必等编辑器做完才能开始填。

阶段二:本地编辑器

npm run studio 开浏览器写图文。见 §7。

阶段三:工程化收尾

见 §8。


7. 阶段二:本地编辑器(方向已定,实现待验证)

形态:本地跑的网页应用,零外部服务、零 OAuth。能力:

  • 图形界面填写三种内容类型的字段,带校验
  • 拖入图片 → 自动压缩转 WebP → 写入仓库
  • 自动读取 EXIF 填元数据
  • 一键 git commit + push,推送前跑一遍构建校验

EXIF 提取的实际边界

器材是索尼 A7C II(ILCE-7CM2),不是 interests.ts 里写的富士——那句是占位文案, 阶段一一并改掉。

能自动读到:拍摄时间、机身型号、镜头型号、光圈、快门、ISO、焦距。

读不到地点:A7C II 机身没有 GPS 模块。除非用索尼 Creators’ App 连手机同步位置, 否则 place 字段仍需手填。手机拍的照片则通常带 GPS,可以自动填。 文档里写明这一点,免得实施时误以为能全自动。

实现路径待定,先做验证

方案 取舍
甲:自己写小 studio 内容类型只有三种、字段少,自己写反而简单;能精确匹配工作流;不引入大依赖
乙:用 Keystatic 专为 Astro 做的内容管理层,支持本地模式写文件系统,能省大量工作;但当前版本的实际状态需要先验证
丙:Obsidian / VS Code 插件 零开发量,但字段校验、图片压缩、发布流程全靠自觉,等于没解决「不直观」

结论:先花少量时间验证乙,不行就走甲。 如果 Keystatic 好用,自己写是浪费; 如果它别扭,自己写反而干净。这个验证是阶段二开工的第一件事。


8. 阶段三:工程化收尾

  • CHANGELOG.md 自动生成 —— 提交信息已经在用 feat: / fix: 前缀,直接能生成
  • 站内搜索 —— Pagefind,构建期生成索引,纯静态零后端,与现有架构兼容
  • 年度回顾页 —— 时间线数据天然能做:这一年去了几个地方、读了几本书、拍了多少张
  • 测试补齐:现在只有冒烟,内容类型多了之后需要 schema 校验测试

关于「日志管理」:静态站没有运行时,因而没有运行时日志。 构建日志在 GitHub Actions 里已经有了。这一项没有对应物,不做。


9. 未决问题

  • Keystatic 是否可用(阶段二开工前验证)
  • 年度回顾页的具体形态(等有真实数据后再设计,现在设计等于空想)
  • 首页如何呈现 moments(等时间线页做完,看实际观感再定)