src="https://api.eyabc.cn/api/picture/scenery/?k=315113aa&u=https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_jpg%2FmeG6Vo0MeviavQReBibFWQ4ybGapBq91bOiaV8yq0TAEL0zRUTdfFjSyPd5AjIwh5LGib45GEYTD7FtTibS6hQiaNTGg%2F0%3Fwx_fmt%3Djpeg">
前言
主要介绍了 Next.js 16 的新特性和更新内容。Next.js 16 是一个重大更新,实验性功能转为稳定版,弃用的 API 被移除,框架速度提升。今日前端早读课文章由 @Trevor I. Lasn 分享,@飘飘编译。
译文从这开始~~
异步参数、默认启用 Turbopack,以及实验特性的清理
Next.js 16 正式发布了!这是团队在过去一年里不断测试之后终于将所有内容稳定下来的一次重大更新。实验性功能正式转为稳定版,弃用的 API 被移除,整个框架的速度也得到了提升。
如果你打算升级,这里是主要变化及其意义。
【第3581期】复杂 React/Next.js 应用的数据获取架构
路由参数现在是异步的
你会注意到最大的变化是:路由参数(params)现在变成异步的了。过去组件直接获取 params 或 searchParams 即可,而现在必须用 await 等待。
受影响的包括 pages、layouts、路由处理函数(route handlers),甚至是图片生成函数(opengraph-image、twitter-image、icon、apple-icon 等)。
基本用法看起来很简单:
// Next.js 15 - 同步参数 export default function Page({ params }) { const { slug } = params } // Next.js 16 - 异步参数 export default async function Page({ params }) { const { slug } = await params }
但影响不止页面。Layouts 也需要相同处理方式;在路由处理函数中,segmentData.params 现在是一个 Promise;generateMetadata 中也要先等待 params 和 searchParams 才能使用。
图片生成函数同样受影响,例如当使用 generateImageMetadata 时,params 和 id 都是 promise:
// Next.js 16 - 图片生成 export async function generateImageMetadata({ params }) { const { slug } = await params return [{ id: '1' }, { id: '2' }] } export default async function Image({ params, id }) { const { slug } = await params const imageId = await id // id 现在是 Promise<string> }
为什么要改成异步?
这是为了流式渲染(streaming)和并发渲染(concurrent rendering)。
当参数是同步时,Next.js 必须等所有数据加载完才能开始渲染;异步参数让框架可以在后台解析参数的同时,提前开始页面框架的流式输出。
这在参数来源多样时尤其重要 —— 有些参数可能来自 URL,但也可能需要数据库验证、边缘配置或网络请求。异步模式让 Next.js 在不阻塞渲染的前提下更灵活地优化这些过程。
如果你用 TypeScript,要注意类型变化:
过去是 Params = { slug: string },现在是 Params = Promise<{ slug: string }>,这意味着相关类型会在许多文件中波及,需要逐步调整。
对于需要在客户端组件中使用路由参数的场景,可以用 React 的 use Hook 解包 Promise:
'use client' import { use } from 'react' export default function ClientPage(props: { params: Promise<{ slug: string }> }) { const params = use(props.params) const slug = params.slug }
自动迁移工具(codemod)可以帮你修改大部分代码,但复杂的解构、参数传递链或辅助函数里的使用,可能仍需手动调整。当 codemod 无法确定修改方式时,会插入 @next-codemod-error 注释,提示你需要检查的地方。
【第3487期】在选择 Next.js 之前,应该了解这一点
Turbopack 成为默认打包器
Turbopack 是 Vercel 花费数年、用 Rust 从零重写的打包器。在 Next.js 16 中,它终于成为默认选项 —— 不再需要配置标志,也不是实验功能。只要运行 next dev 或 next build,你就自动在使用 Turbopack。
迁移过程非常简单:
删除脚本里的 --turbopack 参数即可:
// 旧 "dev": "next dev --turbopack" // 新 "dev": "next dev"
性能提升是实实在在的,尤其是在大型应用程序上。开发环境冷启动更快,热更新几乎是即时的;生产环境构建速度也显著提高,随着模块数量的增加,性能提升幅度也会增大。如果正在处理包含数千个模块的项目,您会明显感觉到这一点。
配置上,Turbopack 已从实验区迁出:
// Next.js 15 - 实验性配置 const nextConfig = { experimental: { turbopack: { // 配置项 }, }, } // Next.js 16 - 稳定配置 const nextConfig = { turbopack: { // 配置项 }, }
另一个值得关注的新特性是文件系统缓存(filesystem caching)。
启用 experimental.turbopackFileSystemCacheForDev 或 experimental.turbopackFileSystemCacheForBuild,Turbopack 会将编译结果缓存到 .next 文件夹中。
重启开发服务器或重新构建时速度会明显提升,在 CI/CD 环境中如果缓存 .next 目录,构建会更快。
const nextConfig = { experimental: { turbopackFileSystemCacheForDev: true, turbopackFileSystemCacheForBuild: true, }, }
调试工具也更强大了。运行
npx next internal trace .next/dev/trace-turbopack
可以生成详细的编译时间追踪报告,帮助你定位构建瓶颈。
如果你仍需使用 Webpack,可以通过加参数退回:
"build": "next build --webpack"
此时开发模式仍使用 Turbopack,但构建时会切换到 Webpack。
旧的 Webpack 特性可能需要调整,例如带波浪号前缀的 Sass 导入(~)。
Turbopack 默认不支持,可以用 resolveAlias 处理:
const nextConfig = { turbopack: { resolveAlias: { '~*': '*', }, }, }
如果客户端代码误导入了 Node 模块(如 fs),也可以用 fallback 解决。虽然最好从源头修正导入问题,但 resolveAlias 可以作为临时方案,帮助你顺利完成迁移。
React Compiler 支持
随着 React 19 的发布,React Compiler(React 编译器) 已经进入稳定阶段。Next.js 16 正式支持该功能,并且可直接在生产环境中使用。
React Compiler 会自动为组件添加记忆化(memoization),从而减少不必要的重新渲染。你再也不用到处包 useCallback 和 useMemo 了。
启用方式非常简单:
const nextConfig = { reactCompiler: true, }
它是稳定但可选的(opt-in)功能,是否开启由你决定。性能提升的幅度取决于应用的复杂程度 —— 重渲染频繁、组件结构复杂的项目收益最大。
缓存 API 改进
Next.js 的缓存机制一向让人头疼 —— 太多 “魔法”,开发者往往搞不清到底缓存了什么、何时过期。在 16 版本中,缓存 API 被正式稳定下来,让开发者可以明确控制缓存策略。
这些 API 去掉了 unstable_ 前缀,已准备好投入生产使用。Next.js 的缓存机制从隐式自动转向显式可控。
1、cacheLife:设置缓存存活时间,不再依赖配置文件。比如用户资料可以缓存几小时,股票价格只缓存几秒。
2、cacheTag:引入标签机制。你可以给缓存打标签,之后按标签失效。比如用户更新了资料,只需失效 user-123 标签,而不是清空全部用户缓存。
3、updateTag:解决 “写后读旧数据” 问题。用户提交表单更新资料后,希望立刻看到修改结果。updateTag 会立即使对应标签过期并重新拉取最新数据:
'use server' import { updateTag } from 'next/cache' export async function updateUserProfile(userId: string, profile: Profile) { await db.users.update(userId, profile) // 立即过期并刷新缓存,让用户立刻看到更新结果 updateTag(`user-${userId}`) }
updateTag 与 revalidateTag 的区别:
1、revalidateTag 会标记数据为过期,但允许用户先看到旧数据,后台再加载新数据,适合内容型页面。
2、updateTag 会等待新数据加载完成,确保立即显示最新状态。
refresh 允许从服务端操作中触发客户端刷新。在服务器端修改数据,告知客户端刷新受影响的用户界面
'use server' import { refresh } from 'next/cache' export async function markNotificationAsRead(notificationId: string) { await db.notifications.markAsRead(notificationId) // 刷新客户端 UI,例如更新头部的通知数 refresh() }
导入方式也变得更简洁:
// Next.js 15 - 实验性写法 import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' // Next.js 16 - 稳定写法 import { cacheLife, cacheTag, updateTag, refresh } from 'next/cache'
此外,experimental.dynamicIO 已更名为 cacheComponents 并进入稳定阶段,用于支持 部分预渲染(PPR):
// Next.js 15 const nextConfig = { experimental: { dynamicIO: true, }, } // Next.js 16 const nextConfig = { cacheComponents: true, }
unstable_cache 仍存在 (仍带有不稳定的前缀),用于为具有缓存功能的异步函数进行封装。使用标签和重新验证对其进行配置:
import { unstable_cache } from 'next/cache' const getCachedUser = unstable_cache( async () => getUserById(userId), [userId], { tags: ['user'], revalidate: 3600, // 缓存 1 小时 } )
这些机制与 Next.js 推进的 React Server Components 与 流式渲染 深度契合。当缓存策略变得显式可控时,框架就能更智能地决定何时拉取、何时流式输出、何时直接命中缓存。
Middleware 更名为 Proxy
Middleware(中间件) 在 Next.js 16 中正式更名为 Proxy(代理)。
原因是 “middleware” 在不同框架中含义各异,而 “proxy” 更直观地表达了它的职责 —— 拦截并修改请求。
迁移非常简单:
终端中重命名文件:
mv middleware.ts proxy.ts
然后更新导出函数:
// Next.js 15 export function middleware(request: Request) {} // Next.js 16 export function proxy(request: Request) {}
更重要的变化是:proxy 只在 Node.js 运行时中执行,Edge Runtime 已被移除。如果你依赖 Edge Middleware 进行低延迟请求处理,这会产生影响。
为什么去掉 Edge?
维护两个运行时版本过于复杂。Edge 与 Node.js API 不兼容、依赖限制多。统一到 Node.js 能减少混乱和维护成本。
如果之前用 Edge 中间件做地理分流、A/B 测试或权限校验,你有几个选择:
-
将逻辑迁移到 Node.js 的 proxy;
-
或者移出 Next.js,用 Vercel Edge Functions、Cloudflare Workers 等边缘计算服务实现。
推荐的实践是:
-
在 proxy 中只做路由和重写逻辑,
-
将鉴权和动态逻辑放在 Server Components 或 Route Handlers 中,
既能保持性能,又符合安全分层。
新增的 skipProxyUrlNormalize 配置项可控制 URL 在进入 proxy 前是否标准化,如果你需要保留特定的 URL 格式或查询参数顺序,可以用它。
另一个重大变化:proxy 不再允许返回响应体。以前 Middleware 可以返回 JSON 或 HTML 响应,现在不行了。Proxy 只能进行请求修改、重定向、或转发,自定义响应需在 Route Handlers 或 API Routes 中实现。
示例:旧的认证中间件返回 401 JSON,现在应改为重定向:
// 旧写法(不再支持) export function middleware(request: Request) { if (!isAuthValid(request)) { return NextResponse.json({ message: 'Auth required' }, { status: 401 }) } return NextResponse.next() } // 新写法 - 使用 redirect export function proxy(request: Request) { if (!isAuthValid(request)) { const loginUrl = new URL('/login', request.url) loginUrl.searchParams.set('from', request.nextUrl.pathname) return NextResponse.redirect(loginUrl) } return NextResponse.next() }
这种改动让职责更清晰:Proxy 负责请求拦截,响应生成交给路由。
AMP 支持移除
Next.js 16 彻底移除了 AMP 支持。不再有 useAmp Hook、AMP 页面配置,也没有混合 AMP/HTML 模式。
原因很简单:AMP 使用率大幅下降,而现代浏览器和 Next.js 本身已经足够快。Next.js 的内置优化已经能实现 AMP 过去提供的性能优势,移除它让框架更轻量。
如果你仍在使用 AMP,需要在升级前找到替代方案。好消息是,大多数情况下,Next.js 的默认性能已经不输 AMP。
图片优化更新
Next.js 16 在图片处理方面强化了安全性与性能优化。对 next/image 处理和缓存图像的方式进行了调整,所有这些改进均基于真实数据和安全问题。
本地图片与查询参数
现在,本地图片路径如果带查询参数(例如 <Image src="/assets/photo?v=1" />),必须显式配置,否则会报错:
const nextConfig = { images: { localPatterns: [ { pathname: '/assets/**', search: '?v=1', }, ], }, }
采用基于白名单的安全机制而非宽松模式。如果镜像版本使用查询参数,请提前声明模式。
默认缓存时间
默认最小缓存 TTL 从 60 秒提升到 4 小时(14400 秒)。因为图片重新验证的成本较高(带宽、处理、CDN)。多数图片并不需要频繁更新。
如果你需要更短缓存,可手动设置:
const nextConfig = { images: { minimumCacheTTL: 60, }, }
图片质量与响应式配置
默认图片质量统一为 75,不再生成多档质量版本,减少 srcset 冗余与缓存负担。若你需要多质量档位,可指定:
const nextConfig = { images: { qualities: [50, 75, 100], }, }
默认 imageSizes 中的 16px 已被移除。为什么?使用分析表明,16 像素的图片几乎都是网站图标,这些图标无论如何都不会通过 next/image 。响应式图片的变体更少了。若有需求可自行加回:
const nextConfig = { images: { imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, }
远程图片安全强化
弃用 images.domains,改用带协议的 images.remotePatterns:
// 旧写法(不安全) const nextConfig = { images: { domains: ['example.com'], }, } // 新写法(推荐) const nextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: 'example.com', }, ], }, }
防止意外的 HTTP 加载。使安全性明确化。如有需要,可通过路径名和端口进一步限制。
同时新增 images.maximumRedirects,在获取图像时限制 HTTP 重定向。默认值从不限制更改为 3 次。防止因图像 URL 永远重定向而导致的拒绝服务攻击。禁用重定向(maximumRedirects: 0 )或针对特殊情况增加次数。
对于本地 IP 开发,Next.js 默认情况下会阻止本地 IP 的图像优化。若在本地开发环境中需要加载局域网 IP 图片,可启用:
const nextConfig = { images: { dangerouslyAllowLocalIP: true, }, }
带 “dangerously” 前缀的配置意味着:仅应在私有网络中使用。
运行时配置被环境变量取代
serverRuntimeConfig 和 publicRuntimeConfig 已被移除,请使用 环境变量(Environment Variables) 代替。这种方式更简单,也能在任何环境下正常工作。
迁移方式如下:
# .env 文件 NEXT_PUBLIC_API_URL="/api" # 客户端可用 DATABASE_URL="postgres://..." # 仅服务端可用
客户端变量必须以 NEXT_PUBLIC_ 前缀开头,服务端变量则无需前缀。就是这么简单。
ESLint 集成变更
next lint 命令被移除,现在直接使用原生的 ESLint 即可。
迁移可通过官方 codemod 自动完成:
npx @next/codemod@canary next-lint-to-eslint-cli .
这会更新你的项目,使其直接调用 ESLint CLI,同时删除 next.config.js 中的 eslint 配置项。
升级策略
升级到 Next.js 16 需要谨慎,因为存在多项重大变更。如果贸然升级,可能导致运行时错误或只在生产环境出现的 bug。
1、首先运行自动迁移工具
npx @next/codemod@canary upgrade latest
该命令会自动处理大部分 “机械式修改”:
-
将
package.json更新到 Next.js 16 与 React 19; -
把同步的
params与searchParams转为异步(自动加上await与async); -
将
experimental.dynamicIO重命名为cacheComponents; -
移除缓存 API 的
unstable_前缀。
不过,codemod 基于静态分析,并不完美。遇到不确定的情况时,它会在代码中插入 @next-codemod-error 注释,提示需要人工检查的地方。
2、需要手动修复的常见场景
params被传入辅助函数或中间变量的情况;
-
复杂解构语法(codemod 无法正确推断);
-
组件跨文件导出 / 导入导致类型丢失;
params存入变量后再传递给其他函数使用。
3、系统性测试
升级后要重点测试以下部分:
-
路由处理函数(Route Handlers):
segmentData.params改为 Promise; -
动态页面组件:使用动态路由或
searchParams的页面; -
Layout 组件:如果访问了
params,要验证是否正确 await; -
Metadata 生成函数:确保已等待
params与searchParams。
4、图片处理
-
如果使用带查询参数的本地图片(如
/image?v=1),必须添加localPatterns配置; -
检查远程图片配置,迁移
domains → remotePatterns; -
验证图片缓存是否符合新的默认 TTL(4 小时)。
5、Middleware → Proxy
这不仅是文件改名,还涉及运行时变化。确保逻辑能在 Node.js 环境中运行,尤其是之前依赖 Edge Runtime 的情况。若中间件以前返回响应体(JSON/HTML),需改为使用 重定向(redirect) 或 重写(rewrite),并仔细测试认证流程的体验变化。
6、环境变量迁移
-
所有原先的
serverRuntimeConfig值 →.env中的普通变量; -
所有原先的
publicRuntimeConfig值 →.env中带NEXT_PUBLIC_前缀的变量; -
客户端组件只可访问带前缀的变量。
7、TypeScript 类型更新
所有涉及 params 或 searchParams 的地方类型都变为异步:从 { slug: string } → Promise<{ slug: string }>。
升级后请运行:
tsc --noEmit
在不生成文件的前提下捕获类型错误。
8、测试生产构建
开发模式与生产环境行为可能不同。务必本地运行以下命令验证生产效果:
npm run build npm start
特别关注缓存行为,因为它在开发与生产模式中差异明显。
9、并行路由(Parallel Routes)
这是一个容易忽视的破坏性变更:空插槽(empty slots)现在必须有 default.js 文件。
// app/@slot/default.tsx export default function Default() { return null }
如果插槽可能为空,就要添加 default.js,可以返回 null 或调用 notFound()。
10. 滚动行为变化
Next.js 16 不再在导航时强制覆盖 CSS 的 scroll-behavior: smooth。如果你依赖旧行为(临时禁用平滑滚动),可在根 HTML 元素上添加 data-scroll-behavior="smooth"。
export default function RootLayout({ children }) { return ( <html lang="en" data-scroll-behavior="smooth"> <body>{children}</body> </html> ) }
11、CI/CD 管道调整
-
如果缓存
.next目录,注意目录结构已更改; -
新增
.next/dev目录,用于并发开发与构建; -
若使用 Turbopack 文件系统缓存,需在 CI 中缓存整个
.next。
12、构建适配器(Build Adapters)
Next.js 16 引入了实验性构建适配器 API:experimental.adapterPath 可指向自定义适配器模块。该功能面向托管服务商或框架开发者,用于扩展构建流程。
13、错误监控
异步参数引入后,错误堆栈结构可能不同。请确保错误监控系统能捕获完整的异步调用栈。如果你的错误边界(Error Boundaries)依赖同步错误捕获,可能需要相应调整。
想了解全部细节,请参考官方的 Next.js 16 发布公告。
关于本文译者:@飘飘作者:@Trevor I. Lasn原文:https://www.trevorlasn.com/blog/whats-new-in-nextjs-16
