翻了下之前的折腾记录:
- 2022 年写《Valine评论异常解决》,LeanCloud 国际版
us-api.leancloud.cn下线,靠自定义serverURLs续命; - 2023 年写《Valine折腾再记》,LeanCloud 国际版共享域名
*.avosapps.us对国内 IP 返回 1020 直接被墙,评论提交必失败,FQ 才能用。
但是,LeanCloud 将于 2027 年 1 月 12 日停止对外提供服务,这下又得折腾了。不过,现在有AI辅助,时代进步的具象化,工具真的听得懂人话了,哈哈。
看了一圈,Waline 是现在公认最省心的方案,而且它后端可以自由选择(LeanCloud / MySQL / PostgreSQL / SQLite),正好可以把数据从 LeanCloud 彻底搬出来。
整体方案
- 评论 + 文章阅读量:Waline v3(
@waline/client@3) - 后端数据库:Neon Postgres(免费、serverless,和 Vercel 同生态)
- 部署:Waline 服务端 Deploy 到 Vercel,环境变量连 Neon
- 前端:Gridea Pro 的 NexT 主题,手动接入 Waline 组件
一、数据迁移(最坑的部分)
问题
从 LeanCloud 导出的数据直接灌进 Neon,发现 wl_comment 表是空的,只有最后一行插入成功。
排查
Neon 的 SQL Editor 是单事务执行的:我把建表语句和 34 条 INSERT 混在一个文件里粘贴,前面任何一句报错(语法不兼容),整个事务就回滚了,最后只有末尾单独运行的那行「侥幸」进去了。
更隐蔽的两个字段问题:
- LeanCloud 导出的
insertedAt是{"$date": "2020-..."}这种对象,而 Waline 的wl_comment.insertedAt是TIMESTAMPTZ,需要纯字符串。 pid/rid(父评论、回复对象)在 LeanCloud 里是 objectId 字符串,但 Waline 表里是 INT。直接塞字符串,导入要么报错要么关联错乱。
解决
写了个清洗脚本(Python),做三件事:
- 拆成「先建表、后插数据」两个文件分别执行,避免单事务整体回滚;
- 把
{"$date": ...}提取成 ISO 字符串; - 建立「LeanCloud objectId ↔ 数字 id」映射表,把
pid/rid全部转成对应数字 id。
最终 wl_comment 34 条、wl_counter(阅读量)100 条顺利入库,最后别忘了 ALTER SEQUENCE ... RESTART WITH 重置自增序列。
经验:凡是「导出来少了一半」这种问题,先怀疑单事务回滚 + 字段类型不匹配,别急着怀疑数据库挂了。
二、Vercel + Neon 部署
问题
按官方文档 Deploy 到 Vercel 后,去 /ui/register 注册管理员,直接 500: Not initialized。
排查
看 Vercel 日志,报错栈里竟然有 leancloud-storage!说明 Waline 走偏了——它既识别到 LEAN_* 系列环境变量,又没读到正确的 PG_*。
关键点:
- Waline 读的是
PG_*(有下划线)环境变量,不是POSTGRES_*/PGHOST; - 我之前图方便把 LeanCloud 的
LEAN_*变量也填进了 Vercel,干扰了判断; - 而且 Vercel 的 Environment Variables 得确认覆盖到 Production 环境。
解决
- 删掉所有
LEAN_*变量; - 只保留
PG_HOST/PG_PORT/PG_USER/PG_PASSWORD/PG_DATABASE/PG_SSL; - 注意
PG_PORT=5432、PG_SSL=true(Neon 强制 SSL); - 重新 Deploy,注册成功。
经验:
Not initialized基本就是「没连上数据库 / 读错环境变量」。Waline 默认优先 LeanCloud,一旦有LEAN_*残留就会去连 LeanCloud 然后 500。干净的环境变量是第一步。
三、Gridea Pro NexT 主题接入
这部分坑最多,因为 Gridea Pro 的主题机制和 Hexo 原版 NexT 不一样。
坑 1:白名单机制,模板读不到配置
评论区一直显示的是旧的 Valine,而不是 Waline。
Gridea Pro 有个白名单:只有 config.json 里 customConfig 声明过的字段,才会传给模板。原版 NexT 只声明了 valine,所以即便我在 theme.json 里写了 waline: true、walineServerURL,模板里 site.customConfig.waline 依然是 undefined。
解决:在主题的 config.json 的「评论」分组里补上 waline(开关)和 walineServerURL(地址)两个声明,模板才能读到。
坑 2:评论列表一直 loading(不是数据库!)
前端能打开,但评论列表一直转圈。F12 一看:后端 /api/comment?path=/post/hmjqsl/ 返回 200,数据正常,但浏览器控制台报错 Cannot read properties of null (reading 'insertBefore'),来自 waline.umd.js 的 Vue 挂载阶段。
根因:@waline/client@3/dist/waline.umd.js 这个 UMD 构建,在 Gridea 主题这种「同步脚本 + 复杂 DOM」场景下 mount 会失败(parent 节点为 null)。
解决:改用 ESM 模块方式加载:
<script type="module">
import { init } from 'https://unpkg.com/@waline/client@3/dist/waline.js';
init({ el: document.getElementById('waline'), serverURL: '...', path: window.location.pathname });
</script>
type="module"天然 deferred,DOM 加载完才执行;- 直接传
el: 元素而不是 selector 字符串,避开解析歧义。
坑 3:阅读量接口名和元素 class 都变了
Waline v3 的阅读量:
- 接口是
/api/article(不是 v2 的/api/pageview); - 页面上要放
<span class="waline-pageview-count">0</span>,并初始化时开pageview: true; - 旧主题的
leancloud-visitors-count元素得改名。
坑 4:关于页默认没评论
/post/about/ 是独立模板 about.ejs 渲染的,原本既没评论块也没阅读量。手动在 about.ejs 里引入 comment 块、加上 waline-pageview-count,并把评论区移进内容卡片内部、去掉独立圆角,视觉上更和谐。
坑 5:CDN 与资源
- jsDelivr 在国内被墙,早期
waline.js(ESM)加载不出来;改用 unpkg,或把waline.js/waline.css本地化到images/waline/。 - 注意:Waline v3 主文件
waline.js是 ESM,普通<script src>会报Unexpected token 'export';要么用waline.umd.js,要么用<script type="module">引入 ESM 版。
UI 适配
评论区背景色直接抄主题的 .bg-color:#fffffff2(白底带透明度),和文章卡片融为一体;主题色用 NexT 蓝 #49b1f5,字号 16px、行高 1.8 对齐正文。
经验总结
- Waline 后端选型是关键:直接上 Neon Postgres / MySQL,一劳永逸。
- 数据迁移先看字段类型:objectId→INT、日期对象→字符串,清洗脚本比手动改省事。
- Vercel 环境变量要干净:只留
PG_*,删掉LEAN_*,否则Not initialized。 - 前端 loading 先看 Network 和 Console:后端 200 就不是数据库问题,八成是前端 mount / CDN / 路径。
- Gridea Pro 主题有白名单:
config.json不声明,theme.json写了也白搭。
ps: 这次折腾下来,最大的感受是——能用脚本清洗的脏数据,千万别手搓;能看日志定位的 bug,别靠猜。剩下的,就交给时间检验吧。
更新于 2026年07月25日