Next.js 14、15、16 更新对比
如果只看最近三代,脉络会很清楚:
14:把 App Router 相关能力推到“可以大规模采用”的位置15:开始重写默认行为,尤其 是请求时 API 和缓存语义16:把 Turbopack、缓存模型、代理边界进一步默认化
一张表先看差异
| 维度 | Next.js 14 | Next.js 15 | Next.js 16 |
|---|---|---|---|
| 核心气质 | App Router 体系可落地 | 默认行为重写 | 新主线默认化 |
| 路由与渲染 | App Router、Server Actions 稳定、PPR 预览 | 延续 App Router,但更强调请求时语义 | 导航和预取模型继续优化 |
| 缓存 | 仍带较多旧默认值 | fetch、GET Route Handlers、客户端导航默认不缓存 | cacheComponents、updateTag、refresh、cacheLife 等更完整 |
| 构建链 | Turbopack 大幅推进 | Turbopack 开发态稳定 | next dev / next build 默认 Turbopack |
| 网络边界 | middleware 仍是主叫法 | middleware 继续使用 | proxy.ts 成为新的主命名 |
| React 对齐 | React 18 主线 | React 19 支持 | React Compiler 支持稳定 |
| 升级风险 | 中等,主要是新旧路由并存 | 较高,默认语义变化明显 | 较高,工具链和配置边界继续变化 |
Next.js 14:从理念走向可用
14 的重点主要有三块:
- Server Actions 稳定
- Partial Prerendering 预览
- Turbopack 能力继续前进
这一版的实际意义
13 把新架构提出来,14 才让很多团队开始认真考虑落地。尤其 Server Actions 稳定之后,表单、写操作、缓存重验证这套链路终于更像完整方案了。
适合怎么判断
如果项目仍然在 Pages Router 时代,但开始想试 App Router,14 是一个比较自然的过渡点。
Next.js 15:从新能力扩展转向默认行为调整
15 真正麻烦的地方,不是功能多,而是“以前默认能工作的地方,现在默认行为不同了”。
1. Async Request APIs
这些请求时 API 开始进入异步模型:
cookies()headers()draftMode()paramssearchParams
import { headers } from 'next/headers';
export default async function Page() {
const requestHeaders = await headers();
const userAgent = requestHeaders.get('user-agent');
return <div>{userAgent}</div>;
}
2. 缓存默认值变化
官方说明里 明确提到,以下场景不再默认缓存:
fetch请求GETRoute Handlers- client navigations
这会直接影响:
- 请求次数
- 页面实时性
- 开发时对“静态页面”的判断
- 老项目升级后的体感差异
3. next/form、instrumentation.ts、after()
15 还补了几块很实用的能力:
next/form:让表单提交和导航更顺地接在一起instrumentation.ts:统一服务端监控接入点after():把非关键副作用放到响应结束后执行
Next.js 16:从“支持新路线”到“默认走新路线”
16 的感觉很像正式宣告:前面几年铺的路,现在开始默认启用。
1. Turbopack 默认化
next dev 和 next build 默认都走 Turbopack。
这意味着:
- 脚本里通常不再需要
--turbopack - 如果项目还依赖自定义 webpack 配置,需要单独处理
- 构建链已经不再把 webpack 视为唯一主线
2. middleware 迁移到 proxy
这不是简单改名。官方想强调的是:这一层更像“网络边界和路由拦截点”,而不是一个什么都能塞的通用中间件层。
同时升级文档也明确写了:
proxy的 runtime 是nodejsedgeruntime 不支持proxy- 如果仍然依赖 Edge,需要暂时保留
middleware
3. 缓存 API 更成熟
16 的缓存相关变化比表面看起来更重要。
除了 cacheComponents,还包括:
revalidateTag(tag, profile)updateTag(tag)refresh()cacheLifecacheTag
这意味着数据一致性不再只能靠“大范围刷新”,而是可以开始用更细粒度的缓存标签去组织。