Personal Tech Hub
中英双语个人技术品牌站,集成 Pagefind 搜索、Giscus 评论、暗色模式与 RSS,构建时自动拉取 GitHub Stars 数据
背景 (Situation)
需要一个能同时展示开源项目、深度技术文章与短笔记的个人技术品牌站,面向面试官、招聘者与同行快速了解技术方向。之前使用 Hugo,组件化迭代和 TypeScript 支持不足。
任务 (Task)
构建一个现代化、中英双语、性能优良的个人技术站点,具备:
- 项目展示、博客、笔记三大内容板块
- 中英双语路由与内容关联
- 静态搜索、暗色模式、RSS 订阅
- GitHub Actions 自动部署到 GitHub Pages
行动 (Action)
- 框架选型:从 Hugo 迁移至 Astro 6.x,利用其 Content Collections 管理多语言内容,Tailwind CSS 4.x 实现主题系统
- 内容架构:
translationKey关联中英对应内容;blog/projects/notes/pages四个集合 - 核心集成:Pagefind 静态搜索、Giscus 评论系统、GitHub API 拉取 Star 数与贡献热力图
- 构建流程:GitHub Actions 自动构建 + Pagefind 索引生成,推送至 GitHub Pages
- 关键决策:
- Astro 而非 Hugo → TypeScript 类型安全 + 组件化迭代效率显著提升
- CSS 变量主题方案 → 暗色模式零 JS 开销,WCAG AA 对比度合规
结果 (Result)
- ✅ 双语站点上线,Lighthouse 性能评分 95+,71 个页面被 Pagefind 索引
- ✅ 支持 RSS、全文搜索、暗色模式、GitHub 活跃度展示
- ✅ 构建时间 <30 秒(含 OG 图片生成),首次内容绘制 <1 秒
- 🔜 持续迭代:Plausible 分析、简历下载、邮件订阅
技术文档
技术难点 为什么做这个项目?解决了什么独特问题?
核心挑战:在动态障碍物棋盘上让 AI 活下去
1. 角落食物死循环
食物随机落在死角(被蛇身+障碍物包围),AI 的 BFS 寻路能找到路径,但逃生检查判定“吃完会困死” → 放弃 → 尾随拖延 → 食物不动 → 永远循环。
解决:引入“可达空间 ≥ 蛇长”弱保判据,与“能到尾巴”强保形成双重检查。实测 stall 死因从 50% 降至 3.3%。
2. 障碍物无限累积
每升一级永久 +1 障碍物,棋盘可用空位线性缩小。蛇长到 ~90 节时 53% 的局因撞障碍而死。
解决:障碍物软上限(30 个,超出时替换最旧)+ 阶段闯关(蛇长里程碑清空全部障碍)。
3. 批量统计不阻塞 UI
极速模式下每局 2000+ 步同步 BFS 循环,连续跑 200 局会卡死页面。
解决:每局后 await setTimeout(0) 让出主线程,onProgress 回调实时更新进度条。turbo 下跳过渲染、粒子、DOM 写和音效,单局从 10 秒降至 0.3 秒。
4. 单文件架构的状态边界
游戏、AI、UI 三层全在一个 IIFE 闭包内,批量统计需同时只跑逻辑不跑渲染。
解决:window.__game 暴露只读状态 + 诊断 API,ai.turbo 标志控制 loop 分支(正常 rAF + render vs 同步 while 循环),muted 标志静音叠加层。
方案对比与决策 关键技术选型的权衡与理由
关键选型与权衡
1. 逃生检查:单判据 vs 双判据
选项 A:仅 BFS 到尾(强保证)→ 对角落食物永远 False,死循环。
选项 B:仅可达空间 ≥ 蛇长(弱保证)→ 偶尔误判但能活。
选择:双判据 OR 组合:强保证优先,弱保证兜底。既保留强保证的可靠性,又覆盖死角场景。实测验证:stall 50% → 3%。
2. 尾随策略:最短 vs 最长到尾巴路径
最短路径:BFS 头→尾第一步,绕小圈,蛇身布局重复,食物路径安全性不变 → 卡死。
最长路径:从尾 flood 距离图,头每步选离尾最远的合法方向,绕大圈,布局充分变化 → 食物安全窗口打开。
选择:改为最长生存路径。max 分从 920 → 1990(+116%)。
3. 障碍物处理:无限累积 vs 硬删除 vs 软上限
无限累积:终局必然被填死。
硬删除(清空):后期突然变简单,玩家体验断裂。
软上限(30 个,FIFO 替换):保留紧张感,但上限可控。
选择:软上限 + 阶段闯关(蛇长每 +50 节清空一次)。紧张与爽感交替。
4. 道具 AI:纳入目标 vs 路过吃
纳入目标:AI 会绕路抢道具,增加计算量。
路过吃:道具随机生成,AI 自然行进中吃到,零额外计算。
选择:默认“路过吃”,保留 chaseBonus 开关扩展金色奖励决策。
架构设计 系统架构、数据流与核心设计模式
单文件三层架构 + 极速双模
┌─────────────────────────────────────────────┐ │ snake.html │ ├─────────────────┬───────────────────────────┤ │ 渲染层 │ rAF loop → Canvas 2D │ │ (visual) │ 蛇身 lerp 插值 / 粒子 / │ │ │ 进度环 / HUD DOM 更新 │ ├─────────────────┼───────────────────────────┤ │ 游戏逻辑层 │ step() / reset() / spawn │ │ (game logic) │ 碰撞 / 食物 / 道具 / 障碍 │ │ │ 闯关 / 等级 / 双倍分 │ ├─────────────────┼───────────────────────────┤ │ AI 决策层 │ aiDecide() 五层降级 │ │ (ai core) │ ①→②→③→④→⑤ │ │ │ BFS / 模拟 / 逃生 / 最长路径│ ├─────────────────┴───────────────────────────┤ │ window.__game 暴露接口 │ │ 极速模式:skip render → 同步 while → 批量统计 │ │ 可见模式:rAF + render → 可视化 AI 决策 │ └─────────────────────────────────────────────┘
数据流(每逻辑步)
aiDecide() → applyAiDir() → dir 更新 → step() 碰撞/吃东西/闯关 → updateHUD() → 下一帧
极速模式状态切换
正常:state=playing + ai.turbo=false → rAF loop 每帧推进 1 步 + render
极速:batchRun() → 设置 ai.turbo=true + muted=true → runOneGame() 内同步 while(state==playing) turboStep() → 单局 0.3s 完成 → await setTimeout(0) 让出主线程
💡 提示:点击每个面板展开详细内容。此组件采用 HTML5 <details> 元素,无需 JavaScript。