底层的初始化、数据迁移以及多级缓存搞定后,博客的基础设施基本稳了。接下来,我把精力转回到了读者与作者端的前台体验——如何让随笔记录更轻快,以及如何让前台主题在拥有丰富交互的同时,保持极高的边缘缓存命中率。
为什么我需要“微动态/笔记”?
写正经长文往往需要构思提纲、组织段落、排版配图,写一篇得花上几个小时甚至几天。这种较重的心理负担,很容易让人一拖再拖。
在日常生活中,我经常会有一些零碎的想法、随手拍的照片、一段代码片段,或者只是几句随感。这些内容发长文显得单薄,发在第三方社交平台又容易淹没在算法信息流里。
我希望博客里有一块轻量记录的自留地:
- 发布足够轻便:随手写两句话、传几张图就能直接发;
- 支持多媒体:不仅能发多图,还要有自适应九宫格、灯箱预览,以及音视频内嵌播放;
- 话题聚合:支持正文直接写
#话题进行归类; - 权限灵活:支持发布仅自己可见的私密随感。
基于这些需求,我开始动手开发 typecho-plugin-notes 插件。
1. 坚持不改动原生表结构
在规划数据存储时,我坚持了一个原则:不修改 Typecho 的原生数据库 Schema。
这样能保证系统升级、备份和迁移时始终兼容原生 Typecho 生态,不会因为自定义数据表造成工具链断层。我和 AI 梳理后,复用了 Typecho 原生表结构:
- 主内容表(
typecho_contents):存放正文内容,标记type = 'note',slug = 'note-{cid}',状态沿用原生字段; - 话题关联(
typecho_metas+typecho_relationships):自动提取正文中的话题存入元数据表,标记type = 'note_topic'; - 扩展字段(
typecho_fields):使用note_attachments存放媒体附件的 JSON 数组,使用note_likes记录点赞数。
2. 话题提取与多媒体按 MIME 分流
在编辑笔记时,输入 #设计灵感 或带表情的 #☕️下午茶,保存时后端会自动通过正则扫描提取话题并存入元数据表,前台渲染时包装为高亮标签,点击即可按话题筛选相关动态。
上传的附件在读取时会按 MIME 类型自动分流:
- 图片列表:根据数量自动应用网格布局(单图大图展示、双图并排、3 张及以上自适应九宫格),超出数量时最后一张自动叠加
+N遮罩; - 视频与音频:直接输出 HTML5
<video>和<audio>播放器,并支持后台实时预览; - 普通文件:渲染为下载卡片。
为了避免引入体积庞大的第三方 Lightbox 库,我集成了自己早前开源的轻量图片灯箱库 ViewImage.js(仅 2KB),支持手势缩放、键盘切换与毛玻璃背景过渡。
3. 专属单页管理后台

为了让发布随笔足够随手,我基于系统的 admin:page 钩子做了一个独立的单页管理后台(/admin/plugin/notes):
- 顶部是轻量输入框,支持拖拽文件批量上传;
- 支持切换公开、私密(仅自己可见)和草稿状态;
- 在动态列表中点击评论按钮,可以在弹窗内直接查看访客留言并快速回复;
- 采用
pageSize + 1前看分页算法,省去了每次翻页都要执行的SELECT COUNT(*)全表扫描。
传统 SSR 主题与边缘缓存的冲突
有了微动态与长文后,新的挑战摆在了眼前:前台主题如何渲染?
在把博客搬上 Cloudflare 边缘节点后,很多人都会遇到一个困惑:为什么明明配好了 CDN 缓存规则,前台的缓存命中率依然不高?
深入排查传统的动态主题渲染机制,会发现几个天然冲突点:
- 个性化 Cookie 污染:访客发表评论后,浏览器会携带包含姓名、邮箱、网址的 Cookie。传统 SSR 会把这些信息直接拼在 HTML 表单里输出。如果 CDN 把这个 HTML 缓存了,其他访客就会看到别人的个人信息;
- 待审核评论仅自己可见:刚提交的评论处于待审状态,服务端只有对特定 Cookie 才能把这条评论渲染出来;
- SSR 递归查库消耗:每次渲染文章页,服务端都要在 D1 里做一次深度递归评论树查询。只要有人发评论或换个参数,整页渲染就会击穿缓存;
- 页面跳转白屏:传统的多页跳转每次都重新建立网络连接并解析整个 DOM,体验不够顺滑。
为了解决这些问题,我对默认主题 Warm(typecho-theme-warm) 进行了彻底重构。
主题重构:评论系统的动静分离
解决缓存污染最直接的办法就是:将评论区从 SSR 页面中彻底剥离,改为客户端异步 API 加载。
- 第一阶段(静态文章直出):
访客浏览器→Cloudflare 平台 CDN (0ms CPU / 0 D1)→ 瞬间呈现纯净的文章正文与评论区骨架容器; - 第二阶段(异步评论渲染):
访客浏览器→GET /api/comments?cid=...→ 返回评论 JSON 渲染讨论区,并从前端 LocalStorage 自动回填昵称与邮箱。
1. 服务端跳过评论查询
在 Post.astro 与 Page.astro 服务端渲染阶段,系统检测到当前主题支持异步评论组件时,会直接跳过所有评论相关的数据库查询:
<!-- Post.astro 渲染的评论区域仅输出一个纯净的挂载容器 -->
<section
class="warm-comments"
id="comments"
data-comments-root
data-cid={post.cid}
data-comment-component-load="dwell"
data-comment-initial-load="auto-first"
></section>
无论访客带着什么 Cookie,服务端输出的 HTML 都是完全一致的纯静态内容,可以安全地被 Cloudflare CDN 平台层和 L1~L3 深度缓存。
2. 智能按需加载(Dwell / Scroll)
评论数据不需要在页面打开的第一时间就发起请求。Warm 主题支持三种加载策略:
dwell(悬停/触底按需加载,默认推荐):使用IntersectionObserver监听评论容器。只有当读者把文章阅读到接近底部,或者鼠标悬停在评论区域时,才会真正触发 API 请求;auto-first(自动加载首屏):页面就绪后在后台静默获取第一页;manual(手动点击):显示“加载评论”按钮,用户点击后再拉取。
对于只读短文或快速划过的读者,完全零消耗评论 API 与 D1 读配额。
3. 访客身份本地回填(LocalStorage)
评论表单中“记住我的称呼和邮箱”功能完全移交至前端处理:
- 访客提交评论成功后,浏览器将其暂存在
localStorage中; - 下次打开任意文章页时,客户端 JS 自动读取并填充表单输入框;
- 既保留了便捷体验,又杜绝了服务端 HTML 夹带私人信息的隐患。
4. 渐进式分页与极速跳转
auto-2渐进式分页:在博客首页和笔记流中,第 1~2 页滚动触底时自动无感加载下一页,第 3 页起转为“点击加载更多”按钮,防止读者因快速翻页迷失位置;- InstantClick 预加载:当读者的鼠标悬停在站内链接上时,浏览器在后台提前预取目标 HTML;真正点击时瞬间完成 DOM 局部替换,实现类似单页应用的无白屏切换;
- 图片 CDN 参数优化:在后台配置图片处理参数后,主题在渲染正文图片、封面图和 Feed 时自动追加 WebP 压缩与尺寸裁剪参数,大幅减少首屏加载体积。
改造收益
经过笔记插件与 Warm 主题的这轮重构:
- 公共页面边缘缓存命中率提升至 99.2%,基本只有刚发布新内容后的首次访问需要 Worker 渲染;
- 随笔发布门槛大幅降低,写长文有排版,发日常有微动态,体验非常轻快;
- 首屏加载体验达到秒开,Google PageSpeed 性能评分稳定在 98~100 分。
前台的展示与缓存问题解决之后,每天管理员频繁登录的管理后台,又该如何加速并降低 D1 消耗呢?