我们把生产 SaaS 从 Next.js 迁到了 TanStack Start:LCP 从 3.8 秒降到 0.8 秒
五天、91 个提交、零停机:一个运营中的 SaaS 从 Next.js 迁到 TanStack Start + Cloudflare Workers 获得了什么,以及为此付出的四次生产事故。

六月底,我们把自己的 SaaS——一个带付费订阅、积分系统和 12 种语言的 AI 创作套件——从 Next.js 重写迁移到了 TanStack Start + Cloudflare Workers。五天、91 个提交,支付全程在线。
先上前后对比。同一台机器、同一个网络、Chrome DevTools 本地指标,采集于新旧两个版本同时服务生产流量期间:
| 指标 | 旧站(Next.js) | 新站(TanStack Start + Workers) | 变化 |
|---|---|---|---|
| LCP | 3.84 秒 | 0.80 秒 | 快 4.8 倍 |
| CLS | 0.01 | 0.00 | 都不错 |
| INP | 48 ms | 48 ms | 没变 |
把话说清楚:真正变快的是加载。交互延迟本来就不差,迁完也一模一样——换框架并没有让按钮变得魔法般跟手。旧的部署我们后来已经下线了,所以现在没法让两个站赛跑了;上面的数字是两站同时在线时采集的。
新站的热请求 TTFB 从我们的观测点看稳定在 0.6 秒上下,冷启动约 1.1 秒。把冷启动压到这个数,占了这篇文章一半的篇幅。
为什么迁
不是因为 Next.js 不好,而是我们在意的东西和旧架构给的东西渐行渐远:
- 一个部署目标。 支付、生成 API、SSR、静态资源——我们想全部放在 Cloudflare 上、一条部署命令搞定,而不是一小撮各有各的死法的服务拼在一起。
- 对服务端 bundle 的控制权。 事后看这条是决定性的。下文那个 TTFB 问题之所以修得掉,就是因为我们能看到、并且砍掉发往边缘运行时的每一个字节。
- 编译期 i18n。 12 种语言、每种约 2,600 条字符串。老应用在运行时解析翻译键,缺一个键生产环境就白屏。现在缺键是构建报错。光这一项估计已经提前消灭了十几次事故。
- 不用背在脑子里的类型化路由。 TanStack Router 的类型化文件路由,替掉了一堆全靠约定和记忆维护的胶水。
约束才是这事有意思的地方:生产数据库——29 张表、真实的支付和积分记录——一个字段都不能动;session 必须活着穿过切换、用户零重新登录(保住同一个 auth secret 和 cookie 名,better-auth 让这可行);checkout 五天全程在线。
编译快,不等于打开快
这一节大概会引起争论,所以把论点说精确。
Next.js 最近确实进步明显——但看清楚进步发生在哪里:Turbopack、dev server 启动提速、CI 构建成本下降。这些红利全部流向了开发者。而用户体验到的是运行时:页面画出来之前,有多少框架机器需要加载、解析、执行。在这一层,两个框架下的是相反的注。Next.js 随页面附带一套厚重的运行时——Server Components 的整套机器、好几层隐式缓存、跨服务端/客户端边界的序列化。TanStack Start 在 Vite 之上只带一层薄运行时,其余交给你自己。
这个差异不用只信我们的话。Platformatic 上个月把同一个应用放在两个框架上跑了基准测试——无缓存、AWS、爬坡到 1,000 req/s(测试在此;他们自己卖 Node 运行时,各位自行掂量,但所有框架跑在同一套环境上)。结果:TanStack Start 稳住约 1,000 req/s、平均延迟约 13ms;Next.js 15 只扛住 322 req/s、延迟以秒计;Next.js 16 canary——在做了真刀真枪的运行时修复之后,包括一个要修到 React 本体里的 RSC 序列化瓶颈——恢复到 701 req/s、431ms。该给的功劳给:那是六倍的进步。但也仍然是三十倍的延迟差,在爬一个架构自己挖的坑。
所以我们观点的诚实版本是:近来的 Next.js 在开发上快了很多,在打开上才刚开始追——因为重量是架构性的,一个每请求都要背更多机器的框架,有一条它优化不穿的地板。我们那张 3.84 秒 → 0.80 秒的表只是一个团队的数据点,但它和公开基准指向同一个方向。如果你的 LCP 在 Next.js 上本来就漂亮,这个论点与你无关;我们的不漂亮。
4.8 倍到底从哪来
没有单一魔法,三件事叠加。
第一,服务端 bundle 减肥——在 Workers 上这不是选修课。 冷 isolate 解析的每一 MB JavaScript 都是用户脸上的延迟,这张账单 Node 服务器从来不会递给你。我们上线后的第一版 TTFB 是丢人的 2.8 秒,而第一版诊断——"上边缘缓存"——是错的。整套缓存方案都写完了,被证据枪毙:真正的开销是冷启动解析臃肿的 SSR bundle。那份方案文档现在还躺在仓库里,没合并,当纪念碑。
真正的修法是做减法。一个图标库把整个图标目录拖进了服务端 bundle,换成只含实际渲染图标的静态注册表,一个提交砍掉 3.27 MB;一个 Workers 上永远用不到的数据库驱动(libsql,多数据库抽象层的一部分)也被打了进来,构建期 stub 掉又剥一块。现在总上传约 6.2 MB gzip,对着 Workers 10 MiB 的硬顶——这个顶对我们是真实的产品约束,chat 功能的 markdown/流式渲染依赖链必须永远懒加载、永远不进 SSR。
第二,边缘 SSR,用户和渲染之间没有中间跳。 新栈在 Cloudflare 边缘渲染,Hyperdrive 池化数据库连接,不回源。
第三,首绘之前更少的 JavaScript 干更少的活。 编译期消息解析意味着客户端画出第一个像素之前,不用先解析语言字典。
代价:第一周的四次生产事故
每一次都能追溯到 Workers 运行时的一条物理约束——常驻 Node 服务器上根本没有这些东西。
我们亲手发货的自死锁。 Workers 把每请求并发 TCP 连接限制在个位数,所以我们用 AsyncLocalStorage 做了请求域单连接。标准做法,还能飞快暴露 N+1——在 Workers 上 N+1 不是慢,是致命。然后某条代码路径在事务内部调了一个 helper,helper 又调了一次 db()。普通连接池上这算邋遢但能活,helper 另抓一条连接就是了;请求域单连接上,helper 在等事务持有的那条连接,事务在等 helper 返回。全站生成请求挂死。静默地。无报错,无超时。破案靠的是 pg_stat_activity 里的一行:一个事务 idle-in-progress,一条查询在等同一条连接。从那以后的家规:事务回调里必须传 tx,永远;事务内出现裸 db(),review 直接卡死。
静静地什么都不干的 fire-and-forget。 Workers 上,响应一返回 event loop 就冻结。没 await 的 promise 不会失败,它们直接停止存在。我们有一批生成后的记账逻辑,在 vite dev 里跑得完美,在生产上什么也没干。ctx.waitUntil() 是存在的,但在框架的 server function 里你经常拿不到执行上下文。所以规矩变成了:await 一切。真正可延迟的活进队列或 cron,绝不挂在悬空的 promise 上。
会挂起而不是报错的数据库路径。 Workers 直连 Supabase 连接池会间歇性 TCP 挂起——握手干干净净,然后沉默。这比报错糟糕,因为上游的一切超时要么迟到要么缺席。在这套栈上,数据库前面的 Cloudflare Hyperdrive 不是优化项,是必需基础设施。我们把配置包成了幂等脚本,让它不可能处于配了一半的状态。
被一个活人抓包的迁移遗漏。 老应用通过 better-auth 的 databaseHooks 发注册赠分。这个钩子活在配置里,不在任何路由文件里,于是我们逐页面、逐接口的迁移清单从头到尾没碰到它——新用户静默拿到零积分。发现的方式是一位目录站审核员注册试用,然后告诉我们什么都没收到。补回钩子、补发账号,把真正的教训写下来:迁移框架时,把活在配置里的副作用单独盘点一遍。auth 钩子、中间件、定时任务、webhook 注册。按路由列的清单,漏掉的恰恰是不住在路由里的东西。
关于大部分活是 AI agent 干的
坦白讲:这次迁移的大头——移植本身、约 31,000 条字符串的 i18n 转换、上面那些事故的修复——是用 Claude Code 做的,人负责审 diff 和控制部署。让这件事可行的不是代码生成,是围着它的护栏:一个物理上阻止对生产库执行迁移命令的脚本、typecheck 和 build 作为不可协商的关卡、部署永远手动、以及在任何事被称为"完成"之前对生产环境做真实验证——curl 真实页面、读真实响应头。上面那个死锁是 agent 写的。也是 agent 从 pg_stat_activity 的输出里找到并修掉的。诸位自行品评。
还会再来一次吗
会,而且第二次会更快。对我们这个规模的团队,背着的复杂度早就悄悄超过了它的回报,而数字很难反驳:LCP 快 4.8 倍、一个部署目标、12 语言构建在发布前就抓住缺键。
但要睁着眼进场。Workers 不是"冷启动更快的 Node",它是另一套物理学:连接是稀缺品,event loop 与响应同死,bundle 的每个字节都躺在延迟关键路径上。速度是真的,学费也是真的。
文中的应用是 OVOV——图像、视频、音乐、语音的 AI 创作套件——想看成品可以去逛逛。
