当前结论
本博客目前采用 Twikoo 作为默认评论区,后端运行在 Cloudflare Worker,评论内容放在 D1,评论图片和头像放在 R2;Quartz 页面只负责加载和定制界面。访客可以使用昵称和邮箱留言,不必先登录 GitHub。GitHub 评论仍作为同一标题下的可选入口保留。
为什么从 GitHub 评论切换
最初的评论入口偏向 GitHub。它的优点是身份和垃圾评论控制比较直接,但访客必须拥有 GitHub 账号并完成登录,留言门槛比较高。Giscus 仍然适合喜欢 GitHub Discussions 的读者,不过不适合作为唯一评论方式。
Waline 的体验更接近传统博客评论区,但之前的部署依赖 Vercel,运行环境、数据库和迁移关系没有被清晰地统一管理。Artalk 可以自托管,可是放在 NAS 后再通过 Cloudflare Tunnel 访问时,速度和稳定性不够理想。综合考虑后,选择了部署资料较成熟、可以直接使用 Cloudflare 基础设施的 Twikoo。
这次调整的目标有三个:
- 访客可以直接用昵称、邮箱和正文留言,不强制 GitHub 登录;
- 评论数据和图片有明确的存储位置,不和静态博客文件混在一起;
- 前端仍然保留 Quartz 的设计语言,并且能够修复 SPA 路由、头像、邮件等实际问题。
整体架构
评论系统被拆成前端、接口、数据库和对象存储四层:
访客浏览器
↓
Quartz 静态页面(博客前端)
↓
Twikoo 客户端脚本
↓
Cloudflare Worker:twikoo.brmys.cn
├── D1:评论、回复、点赞和管理数据
└── R2:评论图片、头像等上传文件Quartz 和 Twikoo 后端是两个可以独立发布的部分。以后即使把博客前端从 Vercel 换到 Cloudflare Pages,也不需要迁移评论数据;只要前端继续请求同一个 Worker 地址即可。
Cloudflare 后端做了什么
后端部署的核心步骤是:
- 在 Cloudflare 创建 D1 数据库和 R2 存储桶;
- 使用 Twikoo 的 Cloudflare 部署模板创建 Worker;
- 初始化 Twikoo 所需的数据表,并绑定 D1;
- 绑定 R2,用于评论图片和头像上传;
- 配置博客域名的跨域访问和 Worker 自定义域名;
- 用健康检查、版本接口、评论读取和 CORS 请求逐项验证。
Quartz 中只需要把 Worker 地址传给组件:
Component.TwikooComments({
envId: "https://twikoo.brmys.cn",
lang: "zh-CN",
version: "1.6.44",
})这里的 envId 是公开接口地址,不是管理密码。真正需要保护的是 Twikoo 管理密码、QQ 邮箱授权码、飞书机器人 Webhook 等后端配置,它们不应该写入前端代码、文章或 Git 仓库。
Quartz 前端是如何接入的
本次没有直接把 Twikoo 的默认页面原样嵌入,而是在 Quartz 中增加了三个层次的定制:
TwikooComments.tsx:输出评论区容器、标题和 GitHub 评论切换按钮;twikoo.inline.ts:加载客户端、处理 SPA 页面切换、头像上传、邮件配置和管理面板编辑;twikoo.scss:负责评论区布局、头像裁剪、深浅色主题和管理面板样式。
评论标题使用“评论或留言”,默认显示 Twikoo。标题旁边保留 GitHub 评论按钮,只有读者主动点击时才延迟加载 Giscus。这样博客首次打开不会因为 GitHub 网络问题卡住,Twikoo 也不会和 Giscus 争夺页面空间。
这次解决的几个实际问题
1. 页面出现巨大图案或像乱码
最开始只加载了 Twikoo 的 JavaScript,没有同步加载相同版本的 CSS,浏览器就会把表单和 SVG 以未样式化状态展示出来。后来前端脚本增加了 Twikoo CSS 的 CDN 回退顺序,并在 CSS 加载后再初始化组件,解决了这个问题。
2. 点设置、点赞或评论后页面变形
Quartz 使用了 SPA 导航。Twikoo 内部有一些 href="#" 的关闭、点赞、回复和设置操作,点击后会被 Quartz 当成页面锚点处理,造成组件重新渲染或样式消失。
现在会对 Twikoo 内部的 # 链接阻止默认锚点跳转,同时保留 Twikoo 自己的点击逻辑,所以这些操作不会再触发博客路由。
3. 头像显示不全
某些主题样式给头像图片设置了过大的 max-width,图片虽然加载成功,却被压缩或只显示一部分。现在头像容器和图片都使用相同的宽高,配合 object-fit: cover,头像可以稳定地显示为正方形。
4. 头像上传和评论图片上传
Twikoo 默认可以根据邮箱和昵称生成头像;如果访客点击评论表单左侧头像,则可以从本地选择图片。图片会通过 Worker 的上传接口写入 R2,浏览器只在本地保存最近使用的头像地址,发表评论时再把头像地址补充到提交数据中。
评论正文里的图片上传也走 R2,而不是依赖某个未配置的图床服务。因此看到“博主未配置图床服务”时,应该检查 Worker 的 R2 图片上传配置,而不是要求访客填写图床地址。
5. QQ 邮件通知
Twikoo 的邮件通知在 Worker 端发送。QQ 邮箱使用 SMTP 的 smtp.qq.com:465 加密连接,发送者地址和 SMTP 用户名保持一致,密码位置填写邮箱授权码。
前端管理面板已经做了简化:选择 QQ 后,主机、端口、TLS、主题和模板等不相关字段会隐藏,避免误填。Worker 端还修正了 SMTP 的发送顺序,必须先完成 MAIL FROM,再执行 RCPT TO,最后提交邮件正文,否则会出现 SMTP 503 或 RCPT TO 失败。
6. 管理面板和内容编辑
Twikoo 原有管理面板在博客主题下对比度较低,所以增加了独立的浅色/深色表面、输入框、标签、按钮和评论卡片样式,并扩大了弹窗可用区域。
管理面板的评论卡片增加了“编辑”入口。编辑内容会先经过标签、属性和 URL 的白名单清理,再提交给 Twikoo 管理接口,避免为了方便编辑而把任意 HTML 直接写回数据库。
飞书通知应该放在哪里
如果要把新评论通知到飞书,推荐使用 Twikoo 后端的 Pushoo 通道:在 Worker 的环境变量中设置 Feishu 通道和机器人 Webhook,通知由 Worker 发出,前端只负责提交评论。
飞书 Webhook 和邮箱授权码都属于后端秘密,不能放在 Quartz 的 quartz.layout.ts、浏览器 localStorage 或公开文章中。当前实现已经预留了邮件通知能力;飞书通知是否启用,只需要继续配置 Worker 端的通知变量并做一次测试,不需要重新设计评论区。
数据到底存在哪里
评论文本、回复、点赞和管理状态在 Cloudflare D1;评论图片和头像文件在 Cloudflare R2;静态博客页面仍然由 Quartz 构建后发布。GitHub 仓库不会因为 Twikoo 评论而保存评论正文,也不需要把访问令牌放进博客。
这也意味着迁移时要分开处理:迁移博客前端只需要重新部署静态站点;迁移评论区则需要备份 D1 和 R2,并根据目标平台的数据结构导入。Waline 的旧数据不会因为切换到 Twikoo 自动出现,后续如有需要,需要单独做一次字段映射和导入验证。
目前的使用建议
每次修改 Worker 或 Quartz 评论代码后,先在预览站验证以下动作:发表评论、回复、点赞、设置、头像上传、正文图片上传、管理面板编辑和邮件测试。确认无误后再发布正式博客。
当前方案的核心取舍是:用 Cloudflare 的托管能力换取较低的运维成本,用 Quartz 的少量定制代码换取更符合博客主题的界面;而 GitHub 评论作为备用入口保留,避免某一条网络链路异常时完全没有评论功能。
🔐 GitHub 评论(Giscus)
正在连接 GitHub 评论…