跳转到内容
写作

用 Astro Content Layer 管理长期笔记

从本站的目录、frontmatter 和公开查询出发,说明如何统一发布边界,并在构建后检查草稿是否泄露。

常青
文章目录

笔记只有几篇时,直接遍历文件就能生成列表。入口变多以后,难点变成了保持一致:一篇草稿不应该从首页消失,却仍然出现在标签、搜索或 RSS 中。

本站把规则集中在三个地方:内容目录、frontmatter 校验,以及公开内容查询。下面记录的是当前仓库的实现,方便以后增加页面时沿用同一条边界。

一份内容,各个页面共享

跳转到“一份内容,各个页面共享”
src/content/docs/notes/ 正式笔记的 Markdown / MDX
src/content.config.ts 内容字段与校验
src/data/taxonomy.ts 分类与稳定标签
src/lib/content.ts 公开查询、排序与相关推荐
src/pages/ 首页、索引、标签与 RSS

文章详情交给 Starlight;首页和聚合页使用 Astro 页面。网页工作台发布的正式文章也进入 notes/,不会另建一份公开内容集合。

这个选择让本地写作与网页写作共享同样的构建校验。它的代价是正式文章需要等构建完成才能上线;灵感流另有自己的发布快照,并不进入这套静态文章查询。

在入口检查容易出错的字段

跳转到“在入口检查容易出错的字段”

一篇笔记的最小结构如下:

title: 用 Astro Content Layer 管理长期笔记
description: 从本站的内容目录和公开查询出发,记录可重复使用的笔记组织方法。
date: 2026-05-09
updated: 2026-09-26
category: 软件开发
tags:
- astro
status: growing
draft: false
featured: false

本站 schema 会检查摘要长度、分类、标签和成熟度;空标签、未登记的标签以及重复标签会导致校验失败。这些规则防止的是结构错误,文章内容是否准确仍然需要审阅。

date 保留首次发布时间,updated 只在实质修订时修改。draft 控制是否公开,status 告诉读者内容处于什么阶段。这两个字段解决不同的问题,具体例子见 把笔记状态当成阅读提示。

公开查询只保留一个入口

跳转到“公开查询只保留一个入口”

首页、列表、标签和 RSS 都从下面的查询取数据:

export async function getPublishedNotes() {
const notes = await getCollection(
'docs',
({ id, data }) => id.startsWith('notes/') && !data.draft,
);
return notes.sort(
(a, b) => b.data.updated.getTime() - a.data.updated.getTime(),
);
}

这段代码来自本站的 内容查询模块。增加一个聚合入口时,先调用它,再做分类或标签筛选。

注意:这个函数负责聚合查询,详情路由仍由 Starlight 的草稿规则处理。因此,检查函数返回值不能代替检查完整构建产物。

构建结束后,再检查发布边界

跳转到“构建结束后,再检查发布边界”

仓库保留了一篇 draft: true 的验证笔记。它的作用是让“草稿不能公开”成为可检查的条件:

  • 草稿详情目录不存在。
  • 首页、笔记列表、标签页和 RSS 没有草稿标题或链接。
  • Sitemap 不包含草稿路径。
  • 搜索产物不把草稿作为可发现的内容。

同时检查站内链接和文章锚点,能发现另一类问题:页面构建成功,但重命名后仍然指向旧地址。本站将这类产物检查与接口测试分开运行,前者检查读者能看到什么,后者检查发布操作怎样改变数据。

本地笔记以文件路径形成地址,所以修改标题不需要移动文件;如果修改文件路径,就需要安排旧地址的重定向。标签使用显式 slug,显示名可以单独调整。

网页工作台使用草稿 UUID 作为正式笔记的文件名。这样的地址不够易读,但不会随着标题变化,也便于把同一草稿的后续更新写回同一个文件。今后若引入可编辑 slug,需要同时设计重名处理和旧地址兼容。

  • 2026-09-26:补充本站目录、实际查询代码、构建验收条件,以及网页写作的边界与 URL 取舍。
  • 适用范围:当前静态 Astro + Starlight 项目;如改动路由或内容加载方式,应重新核对草稿隔离。