三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Next.js 16.3 升级实战:零改动拿下 90% 内存降幅与 5.5× 构建加速,再按需开启瞬时导航

Next.js 16.3 升级实战:零改动拿下 90% 内存降幅与 5.5× 构建加速,再按需开启瞬时导航

背景与痛点

升级前端框架通常是一件「收益不确定、风险很确定」的事:要处理 breaking change、要改配置、要回归测试,最后还可能发现性能没怎么变。Next.js 16.3(2026-08-03 正式发布)的特殊之处在于,它的最大红利几乎零成本——你只需要把next升到最新、确认运行环境达标,下面三类收益会自动生效,不需要改一行业务代码:

  • 开发服务器内存不再失控:长会话next dev编译 50 条路由后,Vercel 自家的仪表盘从 21.5 GB 降到 2 GB(约 90%)。
  • next build复用磁盘缓存:同一份代码二次构建,vercel.com/geist 从 30s 降到 5.5s(约 5.5×)。
  • SSR 吞吐提升:App Router 渲染层改用原生 Node.js 流,官方基准下负载能力提升最高 22%。

更关键的是,16.3 把「Server Components + 客户端缓存」推向一个可落地的形态——Instant Navigations(瞬时导航)。它让 App Router 的路由跳转获得接近 SPA 的响应感,同时保持服务端渲染的好处。本文聚焦两件事:先确认零改动红利真实可验,再给出 Instant Navigations 的开启与回归测试实战,并整理一份可直接照做的避坑清单。

环境准备:先过三道门槛

16.3 是 Next.js 16 的小版本,但因为 16 本身相对于 15 有硬性门槛,升级前先确认运行环境,否则next build会直接报引擎错误

依赖16.x 最低要求说明
Node.js20.9.0+Node.js 18 已不再支持,CI 镜像与自托管运行时都要同步升
TypeScript5.1.0+若用 TS 7 还需单独升级(见下文)
React19.2(随 Next.js 16 提供)Pages Router 使用 package.json 中的 React 版本

确认本地版本:

node -v # 期望 v20.9.0 或更高 npx next --version # 升级前应低于 16.3.0

升级依赖(任选包管理器):

# npm npm install next@latest pnpm pnpm add next@latest 若项目仍用旧版 React,一并升级 pnpm add react@latest react-dom@latest

升级后校验版本:

npx next --version # 16.3.0

如果需要让 codemod 帮你处理大量机械改动(如异步params/searchParamsmiddleware.ts → proxy.ts、移除next lint等),可运行官方升级助手:

pnpm dlx @next/codemod@canary upgrade latest

注意:codemod 不会覆盖所有迁移项。有自定义 webpack 配置、images.qualities非默认值、并行路由缺default.js等情况,仍需人工核对。官方升级指南见 https://nextjs.org/docs/app/guides/upgrading/version-16。

红利一:开发服务器内存回收(默认开启,无需配置)

原理

16.3 的「开发内存最高降 90%」来自两项叠加、且默认开启的能力:

  1. Dev 磁盘缓存(16.1 引入):编译产物落到.next/dev/cache/turbopack,重启 dev server 可直接复用。
  2. 内存回收(Memory Eviction):当内存压力上升,Turbopack 把不活跃的中间结果从内存中驱逐,需要时在磁盘缓存里恢复。它依赖上面的 dev 磁盘缓存——关掉磁盘缓存,内存回收也会随之失效。

实测数字(Vercel 官方基准,编译 50 条路由后)

图1:开发服务器内存对比。数据来自 Vercel 官方基准(vercel.com 仪表盘 21.5 GB → 2 GB,nextjs.org 4,600 MB → 840 MB),属厂商基准,实际降幅取决于路由规模与编辑模式。

如何临时关闭(排查用)

正常情况下你什么都不用做。如果在排查缓存行为时想临时关掉内存回收:

// next.config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { experimental: { // 默认开启('full');排查时设为 false turbopackMemoryEviction: false, }, }; export default nextConfig;

红利二:next build 持久化缓存 + 更快的 SSR + TypeScript 7

构建缓存默认开启

16.3 把 16.1 就有的 Turbopack 磁盘缓存从「仅 dev」扩展到了next build,并且默认开启。构建开始时 Turbopack 会先读磁盘上的缓存条目,跳过未变化的编译工作。

图2:Turbopack 文件系统缓存下next build的编译耗时。数据来自 Vercel 自家站点基准(nextjs.org 21s→9.2s、vercel.com/home 66s→46s、vercel.com/geist 30s→5.5s),属厂商基准;增益大小取决于两次构建间依赖图的稳定程度。

为了看得清「红利从哪来」,下面是官方在文档里给出的显式开关(默认值已为true,列出仅作说明与可关闭之用):

// next.config.ts const nextConfig: NextConfig = { experimental: { turbopackFileSystemCacheForDev: true, // 默认 true turbopackFileSystemCacheForBuild: true, // 默认 true(16.3 起) }, }; export default nextConfig;

CI 必须持久化缓存目录

这是最容易踩的坑:构建缓存放在.next/cache里,只有构建前这个目录被恢复,二次构建才会变快

  • 自托管构建:复用同一个工作目录即可。
  • 容器化构建:每次从干净层启动,必须显式挂载或缓存.next/cache,否则拿不到加速。
  • GitHub Actions:缓存.next/cache目录:
# .github/workflows/build.yml - name: Restore Turbopack build cache uses: actions/cache@v4 with: path: .next/cache key: next-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | next-${{ runner.os }}-

关键提醒:若你的构建环境永远不保留.next/cache(比如某些临时 Runner),应把turbopackFileSystemCacheForBuild设为false,避免写出一份永远不会被读到的缓存。另外,缓存不保证跨框架/Node 版本可移植,建议把缓存 key 与package-lock.json及 Next.js 版本绑定。

SSR 吞吐提升(零改动)

App Router 渲染层把 web streams 换成了原生 Node.js 流,省掉了每次渲染的流转换开销。Vercel 官方基准显示,负载下请求处理能力最高提升22%,且无需改任何业务代码。自建 SSR 服务据此重新评估实例数,往往能少开几个实例。

TypeScript 7:一行依赖拿下 10× 类型检查

next build现在可以把类型检查交给 TypeScript 7(Go 重写的原生编译器,约 10× 更快)。它独立于其它特性,是成本最低的单点优化:

pnpm add -D typescript@^7

实战:按需开启 Instant Navigations

Instant Navigations 是一套可选开启的工具,目标是让 App Router 的客户端导航获得 SPA 般的即时响应,同时不丢掉服务端渲染的好处。它建立在 Cache Components 之上,核心是两个开关。

第一步:开启配置

// next.config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { cacheComponents: true, partialPrefetching: true, }; export default nextConfig;

第二步:理解它做了什么

图3:Instant Navigations 原理示意——点击链接后,客户端缓存的「路由外壳」(布局、筛选器、卡片骨架等稳定部分)立即渲染;库存、推荐、个性化等动态数据随后流式补齐。为概念示意,非运行截图;冷缓存下首访仍需服务端先计算一次外壳。

  • Partial Prefetching(部分预取):对同一路由族,稳定外壳只取一次并在客户端缓存;不同链接复用同一外壳,动态值按需补齐。相比「整页全量预取」更省网络。
  • 更好的 ISR:未被构建期预渲染的 URL,也能在首位访客到来时先给一个即时加载外壳,再在后台升级为完整渲染结果。
  • 开发期工具:Instant Insights(自动提示慢导航)、Navigation Inspector(可视化查看导航的加载外壳)两个 DevTool。

第三步:用缓存指令标注数据生命周期

Next.js 16 的use cache指令可以给异步数据函数标注生命周期,使其进入「静态外壳」。最小写法如下(缓存粒度控制cacheLife/cacheTag等详见官方 Cache Components 文档):

// app/products/[id]/page.tsx async function getInventory(id: string) { "use cache"; const res = await fetch(`https://api.example.com/stock/${id}`); return res.json(); } export default async function Page({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; const stock = await getInventory(id); return <ProductView stock={stock} />; }

第四步:用 instant() 写回归测试,锁死导航速度

最实用的部分是官方提供的 Playwright 助手instant()(来自@next/playwright)。它能断言「点击后哪些内容应立即可见」,一旦某次重构把即时 UI 变慢,测试就会失败——防止「今天快、明天慢」。

// e2e/instant-navigation.spec.ts import { expect, test } from "@playwright/test"; import { instant } from "@next/playwright"; test("商品标题应立即可见", async ({ page }) => { await page.goto("/products/shoes"); await instant(page, async () => { await page.click('a[href="/products/hats"]'); await expect(page.locator("h1")).toContainText("Baseball Cap"); }); await expect(page.getByText("12 in stock")).toBeVisible(); });

建议:Instant Navigations 改变了导航与缓存行为,先在功能分支上开启、跑通instant()测试,再上生产。官方指南:https://nextjs.org/docs/app/guides/instant-navigation。

实验特性与避坑清单

实验特性(勿上生产)

16.3 还附带两个实验特性,API 未来可能变动,不建议用于关键生产路径

// 实验:Rust 版 React Compiler(next dev 到就绪页在 v0 上冷构建快 34%、热构建快 46%,仍实验) const nextConfig: NextConfig = { reactCompiler: true, experimental: { turbopackRustReactCompiler: true }, }; // 实验:网络韧性(断网时挂起而非抛错,配合 next/offline 的 useOffline 钩子) const nextConfig2: NextConfig = { experimental: { useOffline: true }, };
// app/offline-banner.tsx "use client"; import { useOffline } from "next/offline"; export function OfflineBanner() { const isOffline = useOffline(); if (!isOffline) return null; return <p>已离线,恢复连接后自动重试。</p>; }

避坑清单

  1. 先升 Node.js,再升 Nextnext build在 Node < 20.9 会直接报Next.js 16 requires Node.js >= 20.9.0,且 CI 镜像、自托管运行时都要同步。
  2. CI 必须持久化.next/cache:否则构建缓存红利消失,构建始终冷启动。
  3. 缓存不跨版本保证可移植:缓存 key 应绑定 lockfile 与 Next.js 版本;框架或 Node 大版本变动时让它失效重建。
  4. 冷缓存首访仍要等服务端:Instant Navigations 定义本身假设缓存已热;首访或客户端首跳可能仍需服务端计算外壳。
  5. 自定义 webpack 配置会让next build失败:16 起next build默认走 Turbopack,发现 webpack 配置会直接报错。要么迁移到 Turbopack 等价选项,要么用next build --webpack临时回退。
  6. Instant Navigations 先在分支验证cacheComponents+partialPrefetching会改变缓存与导航语义,用instant()测试守门后再上线。
  7. 实验特性只用于学习与原型:Rust React Compiler、useOffline 标注为实验,生产路径先等稳定版。

总结与延伸

Next.js 16.3 的价值可以用一句话概括:它把「升级框架」从纯成本项,变成了带可测量回报的动作。零改动就能拿到的三类红利(dev 内存回收、构建缓存、SSR 吞吐)都有官方公开基准背书;而 Instant Navigations 则给出了一个清晰的、可回归测试的「让 App Router 像 SPA 一样快」的落地路径。

可转移的经验:凡是「框架帮你管缓存/构建/渲染」的版本,升级前先确认运行环境门槛,升级后用官方基准数字建立自己的本地测量(尤其是 CI 二次构建耗时与 dev 内存峰值),再决定是否扩大实验特性的使用范围。

延伸阅读(均为官方文档,版本可核):

  • 16.3 发布说明:https://nextjs.org/blog/next-16-3
  • Turbopack 文件系统缓存:https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopackFileSystemCache
  • Instant Navigations 指南:https://nextjs.org/docs/app/guides/instant-navigation
  • 升级到 16:https://nextjs.org/docs/app/guides/upgrading/version-16
← 返回列表