博客重构记

博客重构记

花了两个晚上把博客彻底重构了一遍:后端 44 个任务逐一落地,测试 41/41 全绿。写代码的快乐,就是把坑一个个填平。这篇把整个过程的思路、取舍和踩过的坑都记下来,给未来的自己留一盏灯。

为什么重构

旧版博客是前后端揉在一起的 Express 单体,用了两年之后问题开始集中爆发:

  • 改一处动全身:加一个接口要在三四个文件里同步改,样式和逻辑混在一个目录里;
  • 上线靠手感:没有测试,每次部署都要手动点一遍核心流程,改完心里没底;
  • 主题无法扩展:CSS 变量只有一层,想加「极简阅读」这类第二主题,牵一发动全身。

重构的目标很朴素:让每个服务能独立启动、独立部署、独立测试,同时给未来的功能(评论通知、影音展示、自动部署)留好接口。

服务拆分

最终拆成了三个互不依赖的服务,各自用最合适的工具:

mblog/
├── backend/   # Hono + Drizzle + better-sqlite3,纯 API
├── site/      # Astro 5 SSR,前台渲染
└── admin/     # Vue 3 SPA,后台管理
服务技术栈职责
backendHono + SQLite全部数据接口:文章、评论、友链、统计、RSS
siteAstro 5 SSR前台页面:首页、文章、归档、影音、项目
adminVue 3 SPA管理后台:发文、审评论、同步豆瓣、站点设置

拆分的核心收益是边界清晰:site 只关心渲染,admin 只关心编辑,backend 只关心数据和规则。任何一个服务可以单独换技术栈,不影响另外两个。

踩坑记录

路由 base 双重前缀

后台部署在 /admin/ 子路径下,Vite 配了 base: '/admin/',结果路由又在组件里拼了一次前缀,首页白屏。排查半小时,最后发现是 API 请求路径变成了 /admin/admin/posts

教训:路径只拼一次。base 配置好了之后,所有内部跳转都用相对路径或路由方法,不要手动拼前缀。

FTS 中文分词

SQLite 自带的 unicode61 分词器对中文很不友好:它把连续的中文当一个 token,搜「博客」搜不到「重构博客的笔记」。解决方案是逐字分词——写入时把正文拆成单字再进 FTS 表:

-- 写入时:正文内容 -> 正 文 内 容
INSERT INTO posts_fts(title, content_md)
VALUES ('博 客', '重 构 记 录');

配合 quote() 转义查询词,中文子串搜索就通了。代价是 FTS 表体积变大,但个人博客完全可接受。

数据库迁移

better-sqlite3 不支持在线改表结构,前期加字段全靠 drizzle-kit generate 生成增量迁移。有次手改迁移文件顺序错了,本地库和线上库结构不一致,排查了很久。

教训:迁移文件只能追加,不能修改。改已应用的迁移 = 给自己埋雷。

双主题架构

这次顺便做了双主题:正常主题(暗色科技风,首页内容丰富)和极简阅读(暖色、衬线、时间线)。核心是两个机制:

  1. CSS 变量 + 主题前缀:所有规则都挂在 [data-theme='normal'] / [data-theme='reader'] 下,切换主题只是换 html[data-theme],零 JS 重绘;
  2. localStorage 记忆:首帧前用内联脚本恢复用户上次的选择,刷新不闪主题。

两个主题的首页结构完全不同(模块卡片 vs 干支时间线),所以页面做了双渲染,靠 CSS 显隐切换,互不干扰。

测试与质量

后端用了 Vitest,8 个测试文件、63 个用例覆盖核心路径:

  • 登录与限流(含 429 边界)
  • 文章的增删改查与分页搜索
  • 评论的审核、回复、批量操作
  • 豆瓣抓取的分页与 TMDB 封面匹配
  • markdown 清洗(script / onerror / javascript: 注入)

测试的收益立竿见影:重构最后一天我改了统计接口的聚合逻辑,跑一遍测试立刻发现一个边界 bug。没有测试的改动叫冒险,有测试的改动叫迭代。

后续规划

  • 评论的邮件通知,文章被评论时第一时间知道;
  • 豆瓣影音的自动同步脚本,定时拉取观影记录;
  • 一套自动化部署:git push 后自动构建镜像、滚动更新;
  • 更多主题变量暴露到后台,让换肤不需要改代码。

重构不是终点,是让下一次改动更轻松的手段。写完这篇,继续填下一个坑。

评论

评论加载中…