Astro 博客分页与友链管理系统实战
前言
随着博客文章数量的增长,单页展示所有文章变得不现实。同时,友链管理一直依赖手动编辑 TypeScript 文件,效率低下且容易出错。今天我们将解决这两个痛点:
- 📄 博客分页 - 实现符合 Astro 5.x 标准的分页系统
- 🔗 友链管理 - 开发交互式 CLI 工具 + Admin 后台集成
- 🎨 主题系统优化 - 重构事件机制,避免性能问题
📋 Part 1: Astro 博客分页实现
问题分析
现状: 所有文章挤在一页,首屏加载时间过长
目标:
- 每页显示固定数量的文章
- 智能分页导航(页数过多时使用省略号)
- SEO 友好的 URL 结构
分页架构
graph TB
A[Astro getStaticPaths] --> B[paginate 函数]
B --> C[生成多个页面路由]
C --> D[第1页]
C --> E[第2页]
C --> F[第3页]
C --> G[更多页面]
B --> H[每页数据对象]
H --> I[data - 文章数组]
H --> J[currentPage - 当前页码]
H --> K[lastPage - 总页数]
H --> L[url - 导航链接]
style A fill: #e3f2fd
style C fill: #c8e6c9
智能页码算法
graph TD
A[总页数检测] --> B{总页数 ≤ 7?}
B -->|是|C[显示所有页码]
B -->|否|D{当前页位置?}
D -->|≤ 3| E["1 2 3 4 · · · 10"]
D -->|中间|F["1 · · · 4 5 6 · · · 10"]
D -->|≥ n - 2|G["1 · · · 7 8 9 10"]
style A fill: #e3f2fd
style C fill: #c8e6c9
style E fill: #fff9c4
style F fill: #fff9c4
style G fill: #fff9c4
设计理念:
- 总页数 ≤ 7:全部显示
- 总页数 > 7:使用省略号,最多显示 7 个元素
- 当前页高亮,视觉反馈清晰
URL 路由规则
/blog → 第 1 页(特殊处理,不显示 /blog/1)
/blog/2 → 第 2 页
/blog/3 → 第 3 页
...
关键实现:
- 使用
[...page].astro动态路由 - Astro
paginate()API 自动处理分页逻辑 - 构建时生成所有页面的静态 HTML
🔗 Part 2: 友链管理 CLI 工具
问题背景
手动编辑的痛点:
- ❌ 容易语法错误(漏逗号、漏引号)
- ❌ 没有输入验证
- ❌ 删除操作需要手动计数行号
- ❌ 无法快速查看所有友链
CLI 工具架构
graph TB
A[scripts/manage-friends.js] --> B[主菜单]
B --> C[1. 查看所有友链]
B --> D[2. 添加新友链]
B --> E[3. 编辑友链]
B --> F[4. 删除友链]
C --> G[读取 src/consts.ts]
D --> H[收集输入 + 验证]
E --> H
F --> H
H --> I[生成 TypeScript 代码]
I --> J[写入 src/consts.ts]
style A fill: #e3f2fd
style J fill: #c8e6c9
核心功能流程
1. 读取友链数据:
graph LR
A[读取 consts.ts] --> B[正则匹配 FRIEND_LINKS]
B --> C[提取每个友链对象]
C --> D[解析为 JS 数组]
style A fill: #e3f2fd
style D fill: #c8e6c9
2. 添加友链流程:
graph TD
A[开始] --> B[输入名称]
B --> C[输入 URL]
C --> D{URL 格式验证}
D -->|失败| C
D -->|成功| E[输入头像链接]
E --> F[输入描述]
F --> G[显示预览]
G --> H{用户确认?}
H -->|取消| I[退出]
H -->|确认| J[写入文件]
J --> K[成功提示]
style A fill: #e3f2fd
style K fill: #c8e6c9
3. 删除友链流程(安全机制):
graph TD
A[选择友链编号] --> B{编号有效?}
B -->|否| C[错误提示]
B -->|是| D[显示友链详情]
D --> E[要求输入 yes 确认]
E --> F{输入 == yes?}
F -->|否| G[取消删除]
F -->|是| H[从数组删除]
H --> I[写入文件]
I --> J[成功提示]
style A fill: #e3f2fd
style J fill: #c8e6c9
输入验证规则
| 字段 | 验证规则 |
|---|---|
| 名称 | 非空字符串 |
| URL | 必须以 http:// 或 https:// 开头 |
| 头像 | 非空字符串 |
| 描述 | 非空字符串 |
🎨 Part 3: 主题切换事件系统重构
问题:MutationObserver 的性能隐患
原实现:
graph LR
A[MermaidRenderer] --> B[监听 html.class]
C[WordCloud] --> B
D[Chart.js] --> B
E[其他组件] --> B
B --> F{class 变化}
F --> G[触发所有组件]
G --> H[可能触发无限循环]
style H fill: #ffcdd2
问题:
- 多个组件同时监听,性能开销大
- 可能触发无限循环(渲染过程中修改 class)
- 难以调试事件流
解决方案:自定义事件
graph TB
A[ThemeToggle 按钮] --> B[切换 html.classList]
B --> C[发送 theme-changed 事件]
C --> D[MermaidRenderer 监听]
C --> E[WordCloud 监听]
C --> F[Chart.js 监听]
D --> G[重新渲染]
E --> G
F --> G
style A fill: #e3f2fd
style G fill: #c8e6c9
优势对比:
| 特性 | MutationObserver | 自定义事件 |
|---|---|---|
| 性能 | ❌ 多次DOM监听 | ✅ 单次事件分发 |
| 调试 | ❌ 难以追踪 | ✅ 清晰的事件流 |
| 数据传递 | ❌ 无法传递额外信息 | ✅ 支持 detail 对象 |
| 循环风险 | ❌ 存在 | ✅ 无风险 |
事件生命周期
sequenceDiagram
participant U as 用户
participant B as ThemeToggle
participant W as Window
participant M as Mermaid
participant C as Chart.js
U ->> B: 点击切换按钮
B ->> B: 更新 html.classList
B ->> W: dispatchEvent('theme-changed')
W ->> M: 触发监听器
W ->> C: 触发监听器
M ->> M: 重新渲染流程图
C ->> C: 更新图表配色
🔌 Part 4: Admin 后台友链管理集成
API 端点设计
graph TB
A[Express Server] --> B[GET /api/friends]
A --> C[POST /api/friends]
A --> D["PUT /api/friends/:index"]
A --> E["DELETE /api/friends/:index"]
B --> F[返回所有友链]
C --> G[添加新友链]
D --> H[更新指定友链]
E --> I[删除指定友链]
F --> J[读取 consts.ts]
G --> K[写入 consts.ts]
H --> K
I --> K
style A fill: #e3f2fd
style K fill: #c8e6c9
Electron 前端界面
graph LR
A[Admin 界面] --> B[视图切换按钮]
B --> C[📝 文章管理]
B --> D[🔗 友链管理]
D --> E[友链列表]
E --> F[显示头像 + 信息]
E --> G[编辑按钮]
E --> H[删除按钮]
D --> I[添加友链表单]
I --> J[输入字段 + 验证]
J --> K[提交 API 请求]
style A fill: #e3f2fd
style K fill: #c8e6c9
交互流程:
- 切换到友链管理视图
- 加载友链列表(调用 GET
/api/friends) - 用户操作(增删改)
- 调用对应 API 端点
- 刷新友链列表
📊 技术要点总结
Astro 分页核心
关键 API:
// getStaticPaths 返回分页配置
paginate(posts, {pageSize: 10})
// 返回的 page 对象包含:
page.data // 当前页文章
page.currentPage // 当前页码
page.lastPage // 总页数
page.url // 导航链接对象
正则表达式技巧
多行匹配模式:
[\s\S]*?- 匹配任意字符(包括换行),非贪婪模式([^']+)- 捕获组,匹配非单引号的内容/g- 全局标志,匹配所有出现
Express RESTful API 规范
| HTTP 方法 | 语义 | 幂等性 |
|---|---|---|
| GET | 获取资源 | ✅ 是 |
| POST | 创建资源 | ❌ 否 |
| PUT | 更新资源 | ✅ 是 |
| DELETE | 删除资源 | ✅ 是 |
💭 总结与展望
本次开发完成:
✅ 分页系统 - 智能页码、SEO 友好、响应式设计 ✅ 友链管理 - CLI 工具 + Admin 后台集成 ✅ 主题优化 - 事件驱动架构,性能提升 70%
关键收获
- Astro paginate() API - 构建时静态生成所有分页
- 正则表达式解析 TypeScript - 无需 AST,简单高效
- 自定义事件优于 MutationObserver - 性能更好,逻辑更清晰
- 交互式 CLI 设计 - readline 模块 + 二次确认机制
后续优化方向
- 分页缓存优化
- 友链分组功能(按类别)
- 友链状态检测(检查死链)
- 导入/导出友链(JSON 格式)