10 天上线一个网站,一次不完美的上站经历
- Vercel
- Nextjs
- Cloudflare worker
- Astro
- 04 Feb, 2026
我是使用现有的框架代码进行开发的,原有代码使用的是 nextjs + postgresql 进行开发的。包含了完整的用户管理、内容管理、权限控制等功能模块,也包含了完整的支付功能。
我花了 4 天时间完成了代码开发,然后部署到 vercel 上,数据库使用了 neon 服务。
第一次技术选型失误:vercel + neon
但是我发现网站上线之后,访问速度很慢。经过拆解之后发现,有一个页面 AI代码写的不好,居然有多少个分类就查询了多少次数据库,导致页面加载非常慢。本来应该使用数据库的窗口函数一次搞定的。AI 写的代码还是要 review 啊。
但是首页我发现还是很慢,经过分析,我发现 nextjs 总是发起 rsc 请求,就是加载页面的时候,同时会预加载页面中包含的链接的页面内容,这导致本来已经很慢的页面加载更慢了。于是我就使用 prefetch={false}把 rsc 预加载给关掉了。
但是首页加载还是需要花费好几秒,我怀疑是不是 vercel 和数据库离的太远,发现 vercel 部署在 us-west,但是数据库部署在新加坡。距离太远了,我就把数据库迁移到了 us-west-2 区域,结果首页加载时间从 8 秒降到了 4 秒。但是 4 秒还是太慢了。
我就想了一个方法,把数据库中的内容全部加载到内存中,然后每次请求直接从内存中读取数据。结果首页加载时间从 4 秒降到了 1 秒以内。但是我发现一个问题,网站有时候访问很快,有时候速度有变的很慢。我怀疑是不是网站占用内存太多了, vercel 把nextjs程序 kill 了。然后再次访问的时候就需要重新把数据从数据库中加载到内存中,导致访问变慢。
后面我查了一下资料,其实 Vercel 本质是基于 serverless / edge functions 的架构 —— 没有请求时不会一直占着一个常驻服务器进程。
- 没请求 → 实例会被回收
- 再来请求 → 自动重新拉起(可能有 cold start)
所以
- 在函数里维护内存缓存
- 开 WebSocket 长连接 -启动长期运行的后台任务
这些都不适合 Vercel
所以我把数据放到内存中,部署到 vercel 上技术方案就不对。怪我知识不足啊。
第二次技术选型失误:Cloudflare Container + neon + cloudflare KV
既然 vercel 不适合长期占用内存的程序,我就想容器化部署应该可以吧。容器能够长期占用内存啊。我就把 nextjs 部署到了 Cloudflare Container 上,数据库还是部署在 neon 上。为啥不部署在 Cloudflare Workers 上呢?因为 nextjs 依赖太多 nodejs 原生模块,没法直接部署到 Cloudflare Workers 上。需要使用 open-next进行转换,但是我对 open-next有不好的印象,因为我之前使用过部署到 Cloudflare Workers 上,很折腾人。并且 cloudflare worker 上太多 nodejs 原生模块不支持,导致很多功能没法用,得把模板代码进行大改,我也暂时不想改太多代码,就想到部署到Cloudflare Container 上。
部署到 Cloudflare Container 上之后,首页加载时间依然需要 7 秒。分析之后发现,container
- 默认是 按需启动
- 没有流量时 → 容器会被回收
- 再来请求 → 自动拉起
- 会有冷启动(通常 1~几秒)
并且 Cloudflare Container 的冷启动时间比 vercel 还长,我通过日志发现,从数据库中加载数据到内存中居然花了 3 秒的时间。
我就去掉了把数据加载到内存中的逻辑,改为从 cloudflare KV 中加载数据,但是效果始终不理想,首页加载时间依然需要 5 秒以上。
为了减少成本,我把 container 的最大实例设置为 1,我发现container一直是在加拿大和美国运行,可能是由于我开了 VPN 的缘故,导致container就近启动吧。其他地区的用户距离太远访问会很慢。
所以 cloudflare container 方案技术选型也不合适。
第三次技术选型:cloudflare worker + astro + cloudflare D1
由于我对 open-next 有不好的印象,所以我决定把 nextjs 换成 astro 进行开发。astro 原生支持部署到 cloudflare worker 上,当然 nodejs 的很多原生模块在 cloudflare worker 上还是不支持的,需要将使用到的有些功能模块进行替换。不过还好 codex,它帮助我完成了大部分替换工作。
为了减少数据库的加载时间,我将数据库从 neon 迁移到了 cloudflare D1 上,D1 是 cloudflare 自己的 sqlite 数据库,部署在 cloudflare 的边缘节点上,访问速度会更快一些。
同时我担心访问 d1 还会很慢,我刚开始的时候,就直接把所有页面在 build 的时候就编译成静态 html 了,部署到 cloudflare worker 上访问,这样访问速度会非常快。充分利用到 cloudflare 的边缘缓存。
但是我发现了一个问题,现在只有 50 个页面,build 很快,deploy 到 cloudflare worker 上也很快。但是如果网站内容多了之后,build 和 deploy 的时间会变得非常长,毕竟每次内容更新都需要重新 build 和 deploy。
我就引入了 astro 自带的 ContentCollection 功能,将页面内容从数据库中提取并缓存起来,每次只build 新增的内容页面,build 时间大大缩短了。但是 deploy 到 cloudflare worker 上还是需要很长时间,我也不清楚问题在哪。实在受不了每次新增一些内容之后就重新 build,然后 deploy 得很久,我就把大部分页面改为动态渲染了。
页面直接从 D1 数据库查询数据进行渲染,访问速度居然很快,完全满足我的需求,我去看了 D1 的日志,发现查询都在 10ms 以内。完全出乎我的意料啊。
我还做了一些优化,对于有些不需要更改的页面,第一次访问的时候会生成静态页面,缓存 10 天。这样就不用没有都访问数据库了,减少了数据库的压力,也减少了数据库的请求次数,毕竟 D1访问量超出免费额度也是要收费的。
现在这个状态的网站,是我理想中的网站,我把管理后台也做上去了,后面就不需要在频繁的更新网站内容了,直接在后台管理内容就行了。
后记
这里要说明一下的是,我的另外一个项目之前已经部署在 vercel 上了,但是考虑到访问速度慢,加上看到 vercel 乱收费,在 nextjs 中夹带私货,让你的网站在 vercel 上频繁进行一些不必要的请求导致成本升高后,我就决定将项目迁移到 cloudflare worker 上。由于项目已经上线了,我不想改动太多代码,就直接使用 open-next 进行适配了, 发现 open-next 其实还不错,可能是我之前能力问题,导致我对它产生了偏见。
不过话说回来,如果下次我再开发新的项目,我可能会尝试一下 tanstack start,尽量不再使用 nextjs。