Vercel 自家仪表盘跑 next dev 从 21.5GB 内存掉到 2GB——Next.js 16.3 把开发内存和 CI 构建一起重写了
Vercel 自家仪表盘跑 next dev,编译完 50 个路由,内存从 21.5GB 掉到 2GB。nextjs.org 从 4.6GB 掉到 840MB。升级 Next.js 16.3,你一行业务代码都不用改。
这不是营销文案里的圆整数,是 Vercel 官方博客给出的可复现基准。用的是 Turbopack 在这三个月里做的两件事:磁盘缓存 + 内存回收,而且默认开启。
先说清楚一件事:16.3 里真正让所有存量项目受益的,不是那个被标题党反复刷的 Instant Navigations,而是默认生效的开发内存与构建缓存。前者要手动开两个 flag,后者是你 npm i 完就白拿的。
为什么 next dev 一直是内存黑洞
Turbopack 的核心设计是增量编译:缓存做过的工作,避免重复编译没改的文件。这个设计有个刻意的取舍——用内存换 CPU。缓存越多,重建越省,但内存就越涨。
问题在于,今天的开发时不只是跑一个编译器。IDE、类型检查器、Linter,现在还要再挂一个 coding agent,每一块都在抢系统内存。长期挂在 next dev 上的大项目,内存曲线是一路往上爬的,爬着爬着就撞上 Node 的 4GB 堆上限,然后就是那句熟悉的:
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
这不是代码 bug,是架构层面的账没算平。16.3 补的就是这一刀。
关键招数:把缓存从内存挪到磁盘,再主动回收
16.3 的两把刀是配套的:
- 磁盘缓存(Disk Caching):Rust 打包器不再把构建产物永久攥在内存里,而是写到文件系统,用到时再读回来。这个能力 16.1 就在 dev 里试了,16.3 正式成为默认。
- 内存回收(Memory Eviction):自动把不用的数据从内存里踢出去,防止 dev 会话无限膨胀。
内存回收有个前置条件——开发文件系统缓存必须是开着的。16.3 里这两个选项默认都开着(内存回收的默认值是 auto)。如果你正在排查缓存或 dev 性能问题,可以手动关掉它:
// next.config.js
const nextConfig = {
experimental: {
turbopackMemoryEviction: false, // 默认是 'auto'
},
};
官方给的实测数据(都是编译完 50 个路由后):
| 项目 | 升级前 | 升级后 | 降幅 |
|---|---|---|---|
| vercel.com 仪表盘 | 21.5 GB | 2 GB | ~90% |
| nextjs.org | 4,600 MB | 840 MB | ~82% |
The Register 也报道了一位早期使用者,内存从约 4GB 掉到 1.5GB,评价是「总算回到正常水平了」。
同一份缓存,顺手把 CI 构建也提了 5.5 倍
磁盘缓存不只是给 dev 用的。16.3 里它同时接管了 next build:重复构建时,没变过的产物直接从缓存读,不再重新编译。
官方给出的 Turbopack 构建耗时对比:
- nextjs.org:冷构建 21s → 缓存 9.2s(约 2.3×)
- vercel.com/home:66s → 46s(约 1.4×)
- vercel.com/geist:30s → 5.5s(5.5×)
对天天推 PR、每次都要跑一遍 build 的团队来说,这个数字比内存那 90% 更值钱。文档里还有更猛的曲线:一个 19,000 模块的生产构建,从 40 秒掉到 1.6 秒左右。
这里有个可落地的下一步:不用改任何配置,先 npm i next@latest 升到 16.3,跑一次构建、再跑一次,对比第二次的耗时和 next dev 驻留 1 小时后的内存曲线。这两个数字会直接告诉你有没有必要继续往下接 Instant Navigations。
零代码改动白拿的另外三样
除了内存和构建缓存,16.3 还有几个「升级就有」的收益,全部不需要动业务代码:
- SSR 改用原生 Node.js 流。App Router 的渲染层把 Web Streams 换成了 Node.js 原生流,省掉一道转换。负载测试下,同样的机器能多扛 22% 的请求。副作用是有个明确信号——边缘运行时(Edge Runtime)已经正式废弃,官方在往 Node 技术栈收拢,还在用 edge runtime 的项目要开始规划迁移了。
- TypeScript 7 支持。TypeScript 7 是微软用原生(Go)重写的移植版,号称类型检查快约 10 倍。升级本地依赖就能在 next build 时用上:
pnpm add -D typescript@^7 - 给 AI agent 的版本化文档。跑
next dev时,Next.js 会在项目里维护一个版本对齐的AGENTS.md块,把 agent 指向node_modules里那版文档。agent 读到的是你实际安装的版本,而不是可能过期的训练数据——这个改动很小,但对用 Claude Code / Cursor 写 Next.js 的团队,能直接减少「agent 编了一个不存在的 API」这类返工。
Instant Navigations:真正的主角,但要按路由逐个接
前面都是白拿的。16.3 唯一需要你主动接的,是 Instant Navigations——让服务端渲染的应用,拥有客户端 SPA 那种「点下去立刻有反应」的手感,同时不放弃服务端渲染的收益。
它建立在去年 11 月的 use cache 指令之上,用两个 flag 开启:
// next.config.ts
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true, // 依赖 cacheComponents,单独开会直接报配置错误
};
机制可以这样理解:服务端组件把可静态化的部分预渲染成一个「外壳(shell)」,导航时外壳立刻渲染出来,动态内容在背后继续流。部分预取(Partial Prefetching)把多个小预取请求合并成每条路由一份可复用外壳——官方举例是侧边栏 20 个链接,从 20 次请求收成 1 次缓存外壳请求。
配套还给了四件工具:Instant Insights(自动揪出所有不是「即时」的导航)、Navigation Inspector(在 shell 状态暂停,看用户实际拿到什么)、Playwright 的 instant() 辅助函数(写成回归测试,防止某次重构把导航搞慢)、以及更细的增量静态再生成。
但别一次性全开。官方反馈里明确列了几个坑:
- 静态导出(static export)和 Partial Prefetching 不兼容;
- 全局
styled-jsx样式会在路由之间泄漏; - 在 SST 上自托管 +
cacheComponents,可能直接把服务端渲染搞坏。
Appwrite 给的建议是:默认收益现在就升级,但 Instant Navigations 一条路由一条路由地接。好在官方提供了一个 next-cache-components-optimizer 技能,可以让 coding agent 帮你逐条路由写失败测试、改代码、验证——v0 团队就是这么把首页、聊天、设置页全部做成即时导航的。
结论
Next.js 16.3 这次更新,本质是把「服务端渲染的应用能不能像 SPA 一样跟手」和「dev 到底要不要吃掉你 21GB 内存」这两件积怨已久的事,一起结了。
对绝大多数项目,正确的顺序是:
- 先升级拿白拿的——内存 -90%、CI 构建最快 5.5×、SSR 多扛 22% 请求,零代码改动;
- 试试实验性的 Rust React Compiler——它直接在 Turbopack 里跑,不再经过 Babel,官方称从启动到页面完全加载最多快 46%;
- 最后再一条路由一条路由地接 Instant Navigations,用
instant()写测试兜底。
先把那条内存曲线和第二次构建的耗时测出来。这两个数字,就是你决定要不要继续往下走的全部依据。
评论区
登录后可评论。