Next.js 14 迁移到 Remix 值不值?我算了笔账,省了$1600/年但花了3周

🔑 关键词:Next.js, Remix, 全栈框架, 自托管, 成本优化

📖 摘要:一个独立开发者把SaaS仪表盘从Next.js 14 + Vercel + Supabase迁移到Remix + Fly.io + SQLite的真实成本对比和踩坑记录,包含具体账单数字和7个技术坑的解决方法。

说实话,我到现在也没完全确定这次迁移是不是对的。但账单从每月$144降到$7,这个数字太扎眼了。项目是个B2B的SaaS仪表盘,月活大概2000,日活300左右,用户上传的图片和PDF加起来300GB。原来的技术栈是Next.js 14.0.4(App Router)+ Vercel Pro + Supabase Pro + Cloudinary。Vercel Pro $20/月,Supabase Pro $25/月,Cloudinary因为图片转换和存储,最贵的时候$99/月。加起来$144/月,一年$1728。去年11月我看了下Vercel的用量,带宽超了,额外$40,Supabase的数据库读操作超了,额外$20。那个月账单$204。我就想,我这点用户量,凭什么花这么多?

图片

迁移目标很明确:把托管成本压到$10/月以内。选型上我试了三个组合:Remix + Fly.io + SQLite (LiteFS),Astro + Node adapter + PostgreSQL,还有SvelteKit + Cloudflare。最后选Remix是因为它的loader/action模型跟Next.js的Server Component比,更接近我熟悉的Express那种直觉。Fly.io的定价也简单:shared-cpu-1x 256MB内存的机器$1.94/月,1GB的volume $0.15/月,出站流量前100GB免费,之后$0.02/GB。我跑了两台机器做高可用,加上LiteFS的consul,总共$4.5/月。图片存储换到Backblaze B2,$0.006/GB/月,300GB是$1.8/月。总共$6.3/月,一年$75.6。省了$1652.4。但迁移花了3周,按我时薪$50算,机会成本$6000。第一年其实是亏的。

图片

迁移步骤我记一下,给想折腾的人省点时间。第一步,用npx create-remix@latest起新项目,选Fly.io模板。第二步,把Next.js的app目录下的page.tsx改成Remix的routes下的_index.tsx,layout.tsx改成root.tsx。第三步,数据获取从async function Page()里的fetch换成export async function loader()。第四步,表单提交从Server Actions换成export async function action()。第五步,认证原来用Supabase Auth,换成Remix的cookie session + 自己写的Drizzle ORM查询。第六步,图片上传从Cloudinary换到B2的S3兼容API,用@aws-sdk/client-s3。第七步,部署用fly launch,然后fly deploy。听起来简单,但我踩了7个坑,下面挨个说。

图片

第一个坑:SQLite的写并发。LiteFS虽然支持多节点读,但写只能在一个primary节点。我的应用有个定时任务每5分钟写一次统计表,结果跟用户请求的写冲突,报SQLITE_BUSY。解决方法是加PRAGMA busy_timeout = 5000;,然后把定时任务改成写到一个单独的队列表,用后台worker慢慢消费。第二个坑:Remix的loader默认在服务端跑,不能直接用window或localStorage。我原来Next.js里有个组件在useEffect里读localStorage,迁过来直接报错。得改成useEffect里判断typeof window !== 'undefined'。第三个坑:Next.js的next/image自动优化,换成Remix后我用B2的CDN加?width=800参数,但B2不自动转换,得自己用sharp在服务端生成缩略图。我写了个/api/thumbnail路由,用sharp处理,结果Fly.io的256MB内存跑sharp经常OOM,后来换成@squoosh/lib才稳定。第四个坑:Vercel的ISR(增量静态再生)在Remix里没有直接对应,我用Cache-Control: s-maxage=60, stale-while-revalidate=300加Fly.io的代理缓存模拟。第五个坑:环境变量。Vercel是在dashboard里配,Fly.io用fly secrets set KEY=value,但本地开发要用.env文件,Remix的dotenv默认只加载.env,不加载.env.local,得自己改remix.config.js。第六个坑:监控。Vercel自带Analytics和Speed Insights,Fly.io没有,我接了Sentry免费版(每月5000事件)和UptimeRobot。第七个坑:SEO。Next.js的generateMetadata换成Remix的export const meta,但动态路由的meta要写在loader返回的数据里,我一开始忘了,Google收录掉了30%。

图片

说实话,迁移完之后我最大的感受不是省钱,是“控制权”回来了。以前在Vercel上,我根本不知道我的数据库查询跑了几次,因为Supabase的读操作是按行算的,Vercel的serverless函数每次冷启动都要重新连接。现在我知道每个请求花了多少CPU时间,SQLite的查询计划我也能直接EXPLAIN。但代价是,我得自己管备份。LiteFS的备份我是用litestream同步到B2,每天一次全量,每小时一次增量。有次我误删了表,恢复花了20分钟。在Supabase上点个按钮就行。所以如果你团队没有DevOps,或者你不想碰服务器,Vercel那$20/月其实不贵。我有个朋友做电商,月流水$5万,他用Vercel Enterprise $500/月,眼睛都不眨,因为他的时间比那点钱贵多了。

图片

最后给个判断标准:如果你的月账单超过$50,且你每天花超过1小时在等部署或者调缓存,那可以考虑迁。如果你的月账单低于$20,或者你用的是Next.js的ISR和Edge Functions这种Vercel独有功能,别折腾。我迁移是因为Cloudinary太贵,而且我的用户主要在北美,Fly.io的yyz区域延迟比Vercel的iad低20ms。具体数字:迁移前LCP中位数2.8s,迁移后1.9s。TTFB从450ms降到120ms。但构建时间从Vercel的90秒变成Fly.io的4分钟(因为要传Docker镜像)。这些取舍你得自己算。

图片

🏷️ 标签: