这篇专门整理当前仓库里那些“不属于某一个组件”的部分。
如果说前几篇在讲 Header、主题、滚动恢复、卡片和详情页这些组件,这一篇讲的就是整站骨架:从 0 开始把一个 Astro 站点推到现在这套结构,中间真正稳定下来的内容模型、数据流、路由和构建设置到底是什么。
1. 当前项目的技术底座
最外层配置现在集中在这些文件:
package.jsonastro.config.mjstsconfig.jsonwrangler.jsoncworker-configuration.d.tssrc/env.d.ts
它们分别解决的是:
- Astro 与 Cloudflare Workers 的依赖和脚本
- Astro 的适配器、别名和 Markdown 代码高亮主题
- TypeScript 严格模式与
@/*路径别名 - Workers 入口、静态资源绑定和兼容日期
- Cloudflare 运行时类型
- Astro 在 Cloudflare 运行时下的
App.Locals类型
当前 astro.config.mjs 的关键点只有 3 个:
adapter: cloudflare()@指向src- Shiki 同时声明
github-light和github-dark
这让后面的页面、组件、内容渲染和部署都落到了同一条链路上。
2. 内容模型已经不止一套 articles
这个站点现在不是单一文章流,而是 5 个集合一起工作:
libraryArticleslibrarySeriesEntrieslibrarySeriesworks / worksEnmedia / mediaEnproducts / productsEn
其中:
libraryArticles存中文文库单篇文章librarySeries存中文文库系列元数据librarySeriesEntries读取library/series/*/*下的章节文件works* / media* / products*存双语官网资产
这就是当前内容结构能同时支持:
- 中文原文库
- 双语作品页
- 双语媒体页
- 双语商店与授权页
src/content.config.ts 里还专门做了一个 defineMarkdownCollection(...),把重复的 loader 写法先收拢掉。到这一步,内容结构已经从“一个目录放所有 Markdown”变成了明确分流的内容系统。
3. 站点文案和路径不散落在页面里
当前仓库把路径和文案统一收在 src/site.ts。
这里做了两件非常关键的事:
- 维护中英文
siteCopy - 提供所有路径 helper
路径 helper 现在包括:
getHomePathgetAboutPathgetArchivePathgetArticlesPathgetSeriesPathgetSeriesChapterPathgetOppositeLocale
官网中英文路径前缀统一由 withLocalePrefix() 处理;文库路径则固定归入 /library/...。这样页面组件和数据层都不需要自己拼字符串。
4. 文章数据层已经形成了稳定接口
src/lib/library/articles.ts 负责文库文章这一条线。当前它已经把页面真正需要的动作都整理出来了:
getPublishedArticles()getArticlesStaticPaths()getRelatedArticles(entry, limit)toArticleCardItems(entries, label)
也就是说,页面层不再直接和 getCollection("libraryArticles") 打交道,而是只消费“公开文章”“静态路径”“相关文章”“卡片数据”这些更高层的接口。
这一步非常重要,因为它把 Astro 内容集合原始数据,转成了页面真正能用的站点数据。
5. 系列数据层比文章线更复杂
src/lib/library/series.ts 是当前仓库最像“业务层”的文件。
系列系统之所以比文章复杂,是因为它不仅要读内容,还要解决:
- 系列元数据和章节目录怎么对应
- 章节 slug 怎么生成
- 系列内章节顺序怎么确定
- 章节列表里如何生成 badge
- 如何得到上一章、下一章
当前版本的核心做法,是用 sourceFolder 把系列元数据与章节目录绑定起来。系列文件里显式写:
sourceFolder: "Not-Yet-Manifested"
章节则从文件路径中推导所属目录。这样系列和章节之间的关联,就不是靠文件名巧合,而是靠一条稳定映射关系。
6. 章节 slug 和顺序都在数据层完成
系列章节这一层,当前已经做了几条很关键的规则:
getChapterSourceKey()提取章节源 keygetChapterSlug()允许 frontmatter 覆盖默认 slugsortSeriesEntries()优先按前导数字排序,再按日期和 slug 排序assertUniqueChapterSlugs()防止同系列下 slug 冲突getSeriesChapterContext()生成当前章、上一章、下一章
这几条规则加起来,才让系列页真正具备了“系列目录”和“章节导航”。
7. 归档不是新内容源,而是聚合层
src/lib/library/archive.ts 并没有定义新 schema。它只做一件事:
- 把单篇文章和系列章节合并成一个按发布时间排序的列表
这里的关键不是技术难度,而是边界意识。归档页不应该维护自己的内容来源,它应该只消费已经存在的两条内容流,然后再做聚合。
因此归档层保持得很轻:
- 从
library/articles.ts取公开文章 - 从
library/series.ts取公开系列及章节 - 合并后统一排序
8. 路由文件被刻意做薄
当前 src/pages/ 下的路由文件几乎都很短,这是有意设计出来的结果。
静态页面通常只剩一句:
<HomePageView locale="zh" />
动态详情页只负责声明 getStaticPaths(),然后把 props 交给对应的 page view:
export async function getStaticPaths() {
return getSeriesChapterStaticPaths();
}
这样做之后,路由层只表达两件事:
- 当前页面对应哪个 page view
- 动态路由需要哪些静态路径
剩下的内容获取、章节上下文、页面结构,全部放回组件层和数据层。
9. 从 0 到现在,真正完成的不是“页面变多了”
而是站点内部的分工稳定下来了。
从最初的 Astro + Cloudflare Workers 最小部署,到现在这套结构,中间实际上完成了 5 次抽象升级:
- 有了可部署的 Astro 工程
- 用 content collections 定义了结构化内容
- 用
site.ts统一了多语言文案和路径 - 用
lib/*建起了页面真正消费的数据层 - 用 page views 和组件层把路由、数据、视图拆开
所以当前版本的价值,不只是页面更多,而是整站已经从“几个页面文件”变成了“可继续扩展的内容站架构”。
10. 这篇和其他专题的关系
如果你要从 0 复刻到当前版本,阅读顺序其实可以这样看:
- 先看最简部署教程
- 再看这篇总整理,建立整站结构认识
- 最后按需要去看 Header、主题、滚动恢复、卡片列表、详情页外壳、系列目录这些组件专题
这样就不会再把所有改动揉在一篇文章里,也更接近当前仓库真实的技术分层。