设计笔记
随笔 约 8 分钟阅读

前台体验重塑 —— 从笔记动态到评论动静分离

底层的初始化、数据迁移以及多级缓存搞定后,博客的基础设施基本稳了。接下来,我把精力转回到了读者与作者端的前台体验——如何让随笔记录更轻快,以及如何让前台主题在拥有丰富交互的同时,保持极高的边缘缓存命中率。


为什么我需要“微动态/笔记”?

写正经长文往往需要构思提纲、组织段落、排版配图,写一篇得花上几个小时甚至几天。这种较重的心理负担,很容易让人一拖再拖。

在日常生活中,我经常会有一些零碎的想法、随手拍的照片、一段代码片段,或者只是几句随感。这些内容发长文显得单薄,发在第三方社交平台又容易淹没在算法信息流里。

我希望博客里有一块轻量记录的自留地:

  • 发布足够轻便:随手写两句话、传几张图就能直接发;
  • 支持多媒体:不仅能发多图,还要有自适应九宫格、灯箱预览,以及音视频内嵌播放;
  • 话题聚合:支持正文直接写 #话题 进行归类;
  • 权限灵活:支持发布仅自己可见的私密随感。

基于这些需求,我开始动手开发 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. 专属单页管理后台

paste-1788175580659.png

为了让发布随笔足够随手,我基于系统的 admin:page 钩子做了一个独立的单页管理后台(/admin/plugin/notes):

  • 顶部是轻量输入框,支持拖拽文件批量上传;
  • 支持切换公开、私密(仅自己可见)和草稿状态;
  • 在动态列表中点击评论按钮,可以在弹窗内直接查看访客留言并快速回复;
  • 采用 pageSize + 1 前看分页算法,省去了每次翻页都要执行的 SELECT COUNT(*) 全表扫描。

传统 SSR 主题与边缘缓存的冲突

有了微动态与长文后,新的挑战摆在了眼前:前台主题如何渲染?

在把博客搬上 Cloudflare 边缘节点后,很多人都会遇到一个困惑:为什么明明配好了 CDN 缓存规则,前台的缓存命中率依然不高?

深入排查传统的动态主题渲染机制,会发现几个天然冲突点:

  1. 个性化 Cookie 污染:访客发表评论后,浏览器会携带包含姓名、邮箱、网址的 Cookie。传统 SSR 会把这些信息直接拼在 HTML 表单里输出。如果 CDN 把这个 HTML 缓存了,其他访客就会看到别人的个人信息;
  2. 待审核评论仅自己可见:刚提交的评论处于待审状态,服务端只有对特定 Cookie 才能把这条评论渲染出来;
  3. SSR 递归查库消耗:每次渲染文章页,服务端都要在 D1 里做一次深度递归评论树查询。只要有人发评论或换个参数,整页渲染就会击穿缓存;
  4. 页面跳转白屏:传统的多页跳转每次都重新建立网络连接并解析整个 DOM,体验不够顺滑。

为了解决这些问题,我对默认主题 Warm(typecho-theme-warm) 进行了彻底重构。


主题重构:评论系统的动静分离

解决缓存污染最直接的办法就是:将评论区从 SSR 页面中彻底剥离,改为客户端异步 API 加载。

  • 第一阶段(静态文章直出)访客浏览器Cloudflare 平台 CDN (0ms CPU / 0 D1) → 瞬间呈现纯净的文章正文与评论区骨架容器;
  • 第二阶段(异步评论渲染)访客浏览器GET /api/comments?cid=... → 返回评论 JSON 渲染讨论区,并从前端 LocalStorage 自动回填昵称与邮箱。

1. 服务端跳过评论查询

Post.astroPage.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 主题的这轮重构:

  1. 公共页面边缘缓存命中率提升至 99.2%,基本只有刚发布新内容后的首次访问需要 Worker 渲染;
  2. 随笔发布门槛大幅降低,写长文有排版,发日常有微动态,体验非常轻快;
  3. 首屏加载体验达到秒开,Google PageSpeed 性能评分稳定在 98~100 分。

前台的展示与缓存问题解决之后,每天管理员频繁登录的管理后台,又该如何加速并降低 D1 消耗呢?