Project Update
分区系统设计:让 Notes 和 Projects 支持项目内更新
问题背景
一开始 Notes 和 Projects 都是单层列表:
/study/post-slug/
/projects/post-slug/
这种结构适合“每篇文章彼此独立”的情况,但不适合长期维护一个项目。
例如“个人网站搭建”本身会不断产生更新:
- 内容模型设计
- CMS 接入
- Cloudflare 部署
- 图片灯箱
- About 页面优化
如果每条都直接放在 Projects 列表里,项目页会变得像普通文章列表,而不是项目档案。
目标结构
更合理的结构是:
/projects/
/projects/personal-website-build/
/projects/personal-website-build/astro-content-collections-guide/
Notes 也类似:
/study/
/study/math-modeling/
/study/math-modeling/first-note/
这样可以先创建一个分区,再把具体更新帖放进去。
内容集合设计
分区本身是一个集合:
src/content/project-sections/
src/content/study-sections/
分区内更新帖是另一个集合:
src/content/projects/
src/content/study/
更新帖通过 section 字段关联分区:
section: "personal-website-build"
前台页面设计
分区首页负责展示所有分区:
/projects/
分区页负责展示当前分区内的所有帖子:
/projects/[section]/
帖子详情页负责展示正文:
/projects/[section]/[slug]/
在 Astro 中,对应的文件结构是:
src/pages/projects.astro
src/pages/projects/[section].astro
src/pages/projects/[section]/[slug].astro
getStaticPaths 的核心逻辑
分区页需要根据所有分区生成路径:
export async function getStaticPaths() {
const sections = await getPublishedSections('projectSections');
return sections.map((section) => ({
params: { section: section.id },
props: { section },
}));
}
帖子详情页需要根据每篇帖子生成两层参数:
return projects.map((project) => ({
params: {
section: project.data.section,
slug: project.id,
},
props: { project },
}));
CMS 中如何选择分区
Decap CMS 可以用 relation 字段让更新帖选择所属分区:
- label: "所属项目"
name: "section"
widget: "relation"
collection: "project_sections"
search_fields: ["title"]
display_fields: ["title"]
value_field: "{{slug}}"
这样编辑帖子时不需要手动输入 slug,可以从已有项目分区里选择。
About 页如何使用分区数据
About 页右侧展示最近更新的分区时,逻辑是:
- 读取所有分区。
- 查询每个分区下最新一篇帖子。
- 按最新帖子的日期排序。
- 取前三个分区展示。
这个设计让 About 页面自动反映最近在维护什么内容。
本站实践总结
分区系统的关键不是路由本身,而是“内容关系”的设计。
只要分区和帖子之间的关系清晰,前台列表、详情页、CMS 表单和 About 自动展示都能围绕这一套结构自然扩展。