AstroBlog 是一个以 Astro 静态输出为核心的个人站点。文章、动态、书籍、友链等内容保存在 src/content/,构建阶段生成页面;评论、访客、音乐和 AI 等需要运行时状态的功能拆到 Cloudflare Worker。这个边界决定了项目的目录组织:页面和内容属于构建侧,数据写入和鉴权属于 Worker 侧,后台只是连接两者的操作界面。
运行时和包管理
当前 AstroBlog 使用 Node.js 24.18.0,项目通过 package.json 固定 pnpm 9.14.4。命令应该从项目根目录执行:
# 安装仓库锁定的依赖,并启用项目声明的 pnpm 版本。corepack pnpm install# 启动 Astro 开发服务器,默认包含本地 API 代理。corepack pnpm devpackage.json 中的脚本已经把构建顺序固定下来。pnpm check 执行 Astro 类型检查和样式契约检查;pnpm build 会先检查密钥、样式、评论 Worker、运行时工具和首页壁纸,再生成 BlogRoom 数据、清理构建缓存、生成字体与图标,最后执行 Astro 构建和 Pagefind 索引。新增构建步骤时应当放在依赖它的步骤之前,而不是在部署平台里另外写一套命令。
目录职责
AstroBlog/├─ src/│ ├─ components/ 页面、功能和后台组件│ ├─ config/ 站点、导航、侧栏和功能配置│ ├─ content/ Markdown/MDX 内容集合│ ├─ layouts/ 全局页面骨架│ ├─ pages/ Astro 文件路由│ ├─ plugins/ Markdown、图片和构建插件│ ├─ scripts/ 浏览器端运行时脚本│ ├─ styles/ 分层 CSS 和设计令牌│ ├─ utils/ 内容查询、URL、图片和运行时工具│ └─ workers/ 站内 GitHub 写入代理├─ workers/│ ├─ comments/ 评论、访客和音乐 Worker│ └─ ai/ AI Worker├─ scripts/ 构建、同步和验证脚本└─ public/ 不经过 Astro 图片处理的静态资源src/pages/ 决定 URL,src/layouts/ 决定页面外壳,src/components/ 提供可复用的界面部件。页面不应该直接读取数据库或拼接 GitHub 请求;它们通过 src/utils/ 或 feature 组件取得已经整理过的数据。src/config/ 中的对象会被页面、布局和后台共同读取,修改配置时要注意它可能同时影响构建页面和管理端表单。
Astro 配置入口
astro.config.mjs 负责静态输出、站点 URL、尾斜杠、旧路径重定向、Svelte、Swup、MDX、图标、站点地图、Expressive Code、Markdown/MDX 处理器和 Vite 构建选项。
文章处理器由 unified 创建,remark 阶段加入数学公式、阅读时间、摘要、指令、分节和 Mermaid,rehype 阶段负责 KaTeX、提示框、标题锚点、图注、外链、邮箱保护和 GitHub 卡片。代码块由 Expressive Code 处理,并通过主题选择器和 CSS 令牌适配浅色、深色模式。这里的插件顺序不能随意交换,例如原始 HTML 必须在需要读取 HTML 节点的插件之前完成解析。
Vite 部分还配置了本地 /api 代理、Three.js 依赖预构建、Terser 压缩、CSS 分割、字体不内联和四个构建 worker。生产构建不包含 source map,字体文件也不会被塞进 CSS 的 base64 字符串。修改构建配置时要同时观察 dist/ 体积和 Pagefind 是否仍能找到正文。
配置和内容的边界
站点行为从 src/config/siteConfig.ts 读取。主题默认使用浅色,主题色相为 200;文章列表默认网格布局,允许切换,瀑布流开启,单侧栏时可以使用三列;分页默认每页十篇。配置文件只描述行为,不包含页面结构。要改导航去 navBarConfig.ts,要改侧栏去 sidebarConfig.ts,不要在页面模板里复制配置值。
文章本身放在 src/content/posts/,通过 src/content.config.ts 注册集合。内容集合在构建时生成类型,页面以 CollectionEntry<"posts"> 读取数据。这样文章字段、页面组件和管理后台使用的是同一份契约,不会出现编辑器允许填写一个字段、构建却无法解析的情况。
修改时的基本路径
新增页面时在 src/pages/ 建立路由,再决定是否使用 Layout.astro 或独立布局;新增公共 UI 放在 src/components/,页面专用组件放在 src/components/pages/;新增样式优先放到对应的 src/styles/ 分层文件,并消费现有 CSS 变量;新增内容类型需要同时修改 src/content.config.ts、内容文件和读取它的页面;新增服务端能力则从 workers/ 的路由和 migration 开始。
可以把 AstroBlog 看成一条构建流水线:内容文件经过 Schema 校验和 Markdown 管线变成静态 HTML,Svelte 只在需要状态的地方接管浏览器交互,Worker 处理构建后仍然需要的写入和查询。理解这三层之后,后续修改就能先判断问题属于构建、浏览器还是边缘服务,而不是在所有组件里盲目搜索。
本地检查
# 检查 Astro 模板、Content Collections 类型和全局样式契约。pnpm check# 检查后台内容格式、草稿隔离、taxonomy 和 GitHub 写入边界。pnpm verify:admin# 运行评论路径、访客信号等 Worker 单元测试。pnpm verify:comments# 验证 AI provider 路由、提案解析和索引相关逻辑。pnpm verify:ai# 执行密钥、资源、Astro 页面、Pagefind 和生成脚本的完整构建链。pnpm build开发环境可以直接运行 pnpm dev 查看页面,但评论、访客、AI 和 GitHub 写入依赖 Cloudflare 环境变量,不能只用本地页面判断远程服务是否正常。完成代码修改后,至少运行与修改范围对应的 verify:*,再用完整构建确认生成页面和搜索索引没有回归。
别把生成文件当成源文件
构建会改写几类文件:src/constants/icons.ts 和 adminIcons.ts 由图标扫描脚本生成,字体 CSS 由 generate-font-css 生成,public/room-data/ 由房间数据脚本生成,dist/ 和 Pagefind 索引完全属于产物。排查生成结果时应回到对应脚本和输入目录,不要直接编辑产物来修复问题。比如房间书籍数量不对,应该检查 src/content/bangumi 的 Frontmatter 和 generate-room-data/index.mjs 的筛选,而不是手改 public/room-data/books.json。
路径别名和类型检查
项目的 TypeScript 配置为 @/、@components/、@layouts/ 和 @utils/ 提供别名。新增 import 时优先使用已有别名,避免同一目录出现相对路径和别名混用。Astro 的 astro check 会同时检查 .astro 模板的 props、Content Collection 类型和 Svelte 集成;只运行 tsc 无法覆盖这些模板边界。
配置文件中的值并不都是可以运行时修改的。siteConfig、Markdown processor、Vite 代理和页面开关在构建阶段读取;评论、访客和 AI 的环境变量在 Worker 请求时读取。修改前先判断值的生命周期,再决定是否需要重新构建或重新部署 Worker。