背景:一条硬约束
评论系统是博客的灵魂。但我有条硬约束:不想维护一个评论系统的后端服务,不想半夜被报警叫起来修评论数据库。我要的是一个”部署之后就能忘掉”的方案。
AI 给了三个选项,各有取舍:
- Giscus:评论数据存 GitHub Discussions,零后端。缺点:用户必须登录 GitHub 才能评论,普通访客门槛太高;数据不在自己手里。
- Twikoo:基于 serverless 部署,数据存自己的 MongoDB,支持匿名评论,有管理面板。缺点:AI 推荐的管理员登录方案(Netlify Identity + Git Gateway)早在 2025 年 2 月就被官方废弃;而且 Twikoo 开发者活跃度在下降,最后 release 停在较早的版本。
- Waline:跟 Twikoo 很像,但社区更活跃、文档更全、跟 Twikoo 数据兼容可以直接迁移、有独立的注册页
/ui/register,不依赖第三方认证。
从最开始只实现了Giscus方案,后续修改为了Twikoo方案(此时用的MongoDB),最终选了 Waline + Giscus 双 Tab:默认 Waline(游客免登录,降低评论门槛),可切 Giscus(给有 GitHub 账号的深度用户)。Waline 挂了还有 Giscus 兜底。
选型本身很顺利,坑全在后面的迁移和部署。这一篇就是三段翻车记录:Netlify 404 连猜四轮、Vercel 打转两天、最后靠官方文档半小时救场。
事故一:Netlify 上 404,AI 连猜四轮
背景:把 Waline 的 serverless 函数部署到 Netlify 上新建的 lublog-comments 站点(把 Twikoo 时代的旧仓库改关联过来)。部署完访问注册页 /.netlify/functions/comment/ui/register,一直 404。
时间线(每一轮都是”改完部署一下就好了”):
| 轮次 | AI 的笃定判断 | 结果 |
|---|---|---|
| 1 | 一定是因为 JWT_TOKEN 没设置,注册页才不暴露 | 部署,404 |
| 2 | MONGO_HOST 格式不对,是 JSON 数组不是逗号分隔 | 部署,404 |
| 3 | 变量被 Netlify 强制成 Secret 了,改成 All scopes | 部署,404 |
| 4 | 一定是函数路径前缀没剥离,改 comment.js | 部署,404 |
代价:Netlify 团队免费额度 300 credits/月,每次生产部署约 15 credits,被反复烧。核心问题不是 AI 不会分析,而是它没证据却一副我懂了的样子,让我误以为每次都是最后一次,放心去点部署。
转机:用 Netlify CLI 拉站点配置,发现残留一个 Twikoo 时代的 MONGODB_URI,指向已经停用的旧集群 cluster0。删掉它、又加了路径前缀剥离,重新部署,依然 404。
真根因:官方 Waline Netlify starter 用的 serverless-http 适配器,跟当前 Netlify Functions 运行时不兼容。函数确实被调用了,但请求无法被翻译成 Waline 能路由的格式,于是所有路由(含注册页)全军覆没。前面四轮全猜错了。
结论:迁 Vercel。那里是 @waline/vercel 的原生环境,没有适配层,一次部署大概率就能通。
事故二:Vercel 上打转一整天,参数改了个寂寞
以为换个平台就”一次通”,结果又打转整整一天,直到翻开官方文档才成功。
坑 1:盲目用 latest,撞上 ESM 重构
package.json 里写 "@waline/vercel": "latest",部署后注册页能打开,但一写库就崩:
Error [ERR_REQUIRE_ESM]: require() of ES Module
/var/task/node_modules/@mdit/plugin-katex/dist/index.js
@waline/vercel 在 1.41 版本做了一次 ESM 重构,内部直接发布未编译源码,还依赖了纯 ESM 的 @mdit/plugin-katex。旧版入口 index.cjs 是 CommonJS,require() 一个 ESM 包直接崩。官方文档教程对应的其实是 1.31.x 时代的老写法。我没核对版本就写 latest,等于主动踩进了重构后的雷区。
坑 2:环境变量打转
| 我试的操作 | 结果 |
|---|---|
加 MONGO_URL 完整连接串 | Waline 根本不读,报 No valid storage found |
删掉零散 MONGO_* 只留 URL | 启动直接崩 |
把 MONGO_* 全部加回去 | 恢复能启动但连不上 |
加 MONGO_AUTHSOURCE=admin | 连库时间从 5 分钟变 30 秒,还是不成功 |
加 MONGO_OPT_SSL=true | 同样不成功 |
把 MONGO_PORT 从 27017 改成数组 [27017,27017,27017] | 端口从 :2,:7,:0 修回 :27017,还是连不上 |
最坑的是 MONGO_PORT:think-mongo 期望端口是数组,我把字符串 "27017" 传进去,它按字符拆,给了三个 host 各一个 2、7、0 当端口。日志里那行 host:2, host:7, host:0 我一度以为只是日志被截断,直到看完整日志才发现是真实端口。
坑 3:调超时,纯属治标
连库一直挂起,又去调超时:generic-pool 的 acquireTimeoutMillis 从 3 秒改成 30 秒;函数加 maxDuration: 90;测试函数里写 Promise.race 50 秒强制返回。效果是错误形式变了,失败的本质没变。
最后翻出 Vercel Hobby 计划的文档才明白:函数 maxDuration 默认只有 10 秒,而 MongoDB Atlas 握手要 30 秒以上。这是平台套餐和数据库的物理组合问题,不是任何代码参数能解决的。我调整的所有超时,都是在给一个不可能的组合续命。
真相:两层不兼容
把 Vercel 函数日志完整翻出来,才拼出全貌:
- 驱动不兼容:
@waline/vercel底层think-mongo是 2018 年的包,锁死mongodb@3.5.9驱动,而我的 Atlas 集群是 MongoDB 8.0。老驱动与 8.0 握手直接挂起。用 npmoverrides把驱动升到 6.x,又暴露ObjectID被移除、端口数组、useUnifiedTopology弃用警告等一串兼容伤。 - 平台限制:Vercel Hobby 函数
maxDuration10 秒,MongoDB 握手需要 30 秒以上。即使驱动兼容,这个组合也跑不通。
两层任何一层都判了死刑。而我在环境变量和超时上打转,是因为这两层得把完整日志、版本号、官方文档摊一起看才看得出来,不是看一眼报错就能想到的。
修复:放下执念,走官方路线
实在没辙了,我翻开 Waline 官方部署文档(waline.js.org/guide/deploy/vercel.html),发现官方在 Vercel 上演示的数据库方案根本不是 MongoDB,而是 Neon(PostgreSQL),Vercel 的托管数据库:
- 点文档里的 Vercel 一键部署按钮(官方 example 模板)
- Vercel → Storage → Create Database,选 Neon,自动注入
POSTGRES_HOST等 4 个变量 - 在 Neon 的 SQL Editor 里执行官方
waline.pgsql建表 SQL - Redeploy
- 打开
/ui/register注册,首个用户自动成为管理员
半小时完成,一次成功。原因很清楚:Neon 是 Vercel 托管数据库,连接走内部优化,握手小于 1 秒,10 秒的 maxDuration 绰绰有余。环境变量也是集成自动注入的,连手动配变量这步都省了。
教训
- AI 说已经找到根因时,先要证据。让它贴响应头、函数日志、变量实际值,而不是一句结论。证据不到位,结论就是猜测。
- 改完再部署要算账。部署烧 credits 或费用,先做一次完整的根因分析,把怀疑的点排一排,先试最像的,别每次只堵一个假设去试。
- 能免费验证的,绝不先部署。环境变量、重定向、构建配置都能在部署前用 CLI 或后台核对,不需要用生产部署当试错成本。
- 反复调参 3 轮以上还没好,先查官方文档。环境变量、超时这类参数,调一百次也只是在一个错误方向上微调。
- 版本兼容性是最容易被忽略的根因。2018 的库连 2024 的数据库,报的错往往千奇百怪(404、超时、instanceof、无效端口),没一个是字面意思。核对版本号、看官方支持的组合,比猜环境变量快得多。
- 平台限制是硬约束。Vercel Hobby 的
maxDuration=10s不是 bug,是套餐功能。在这种硬约束下,任何让连接更快的代码努力都是徒劳,应该换方案(换数据库、升套餐、换平台),而不是换参数。 - 适配器或运行时类问题,优先用原生支持的环境。Waline 是
@waline/vercel,在 Vercel 原生跑;硬塞进 Netlify 的serverless-http适配层,就是给自己埋坑。 - AI 过度积极也是病。Netlify 篇是过度自信,Vercel 篇是过度积极:把猜的东西一条条写成能直接执行的参数改动,让你在看起来在推进的循环里烧掉时间。对治方法只有一条:停下来,看证据,查文档。
游客评论无需登录,昵称和邮箱为选填项(邮箱仅用于回复通知)。什么是 Waline?