Astro 博客分页与友链管理记录

博客分页

随着文章数量增长,单页展示所有内容变得不现实。Astro 的 paginate() API 会在构建时静态生成所有分页,路由结构也比较直接:/blog 对应第一页,/blog/2/blog/3 依此类推,使用 [...page].astro 动态路由承接。

// getStaticPaths 中调用 paginate
paginate(posts, { pageSize: 10 })

// 返回的 page 对象
page.data          // 当前页文章数组
page.currentPage   // 当前页码
page.lastPage      // 总页数
page.url           // 前后页导航链接

页码导航的显示逻辑需要处理页数很多的情况。总页数不超过 7 时全部显示;超过 7 时用省略号压缩,最多保留 7 个元素。当前页靠近开头时显示 1 2 3 4 ... 10,靠近末尾时显示 1 ... 7 8 9 10,处于中间时显示 1 ... 4 5 6 ... 10。这样无论文章多少,导航栏都不会撑开。

友链管理 CLI

友链数据存在 src/consts.ts 里,以前每次增删都要手动编辑 TypeScript 文件,容易漏逗号或引号,也没有任何验证。scripts/manage-friends.js 解决了这个问题,提供交互式菜单,支持查看、添加、编辑、删除四个操作。

读取友链时,脚本用正则表达式匹配 FRIEND_LINKS 数组,逐个提取对象字段,不引入 TypeScript 编译器或 AST 解析库。写入时重新生成整个数组的字符串并替换原文件对应区块。

添加友链时,URL 字段会验证是否以 http://https:// 开头,其余字段只检查非空。删除操作有二次确认,需要手动输入 yes 才会执行,避免误操作。

Admin 后台集成

CLI 工具解决了命令行场景,Admin 后台则提供了 Web 界面。Express 服务器暴露四个端点:

  • GET /api/friends 返回所有友链
  • POST /api/friends 添加新友链
  • PUT /api/friends/:index 更新指定友链
  • DELETE /api/friends/:index 删除指定友链

后端读写逻辑和 CLI 脚本共用同一套正则解析方案,都直接操作 src/consts.ts。前端界面在文章管理和友链管理之间切换,友链列表展示头像和基本信息,每条记录带编辑和删除按钮。

主题切换事件重构

原来各个需要响应主题切换的组件(Mermaid 渲染器、词云、Chart.js 图表)都用 MutationObserver 监听 html 元素的 class 变化。这个方案有两个问题:多个观察者同时运行性能开销不小,而且渲染过程中如果修改了 DOM class,可能触发新一轮观察,形成循环。

改成自定义事件后,ThemeToggle 切换主题时发送一个 theme-changed 事件,各组件各自监听这个事件并响应。事件可以通过 detail 携带当前主题名,组件不需要自己再去读 DOM 状态。事件流向清晰,也不存在循环触发的风险。

// ThemeToggle 发送事件
window.dispatchEvent(new CustomEvent('theme-changed', {
  detail: { theme: newTheme }
}));

// 各组件监听
window.addEventListener('theme-changed', ({ detail }) => {
  // 用 detail.theme 重新渲染
});

几点收获

这次开发让我对 Astro 的静态生成机制有了更直观的认识,paginate() 在构建阶段就把所有页面生成好,运行时没有动态计算。用正则解析 TypeScript 源文件虽然不如 AST 严谨,但对于结构固定的配置数组来说够用,也省去了引入额外依赖的麻烦。主题事件的重构则提醒我,MutationObserver 适合监听外部 DOM 变化;对于自己控制的状态变更,自定义事件更合适。