Next.js 16 升级陷阱:并行路由的默认设置与用于代理的中间件
了解为何 Next.js 16 的并行路由槽需要 default.js,如何安全地将 middleware.ts 迁移至 proxy.ts,以及在确认升级完成前应进行哪些测试。
来源: Parallel Routes and proxy.ts in Next.js 16: The Routing Changes That Catch Teams Off Guard
Next.js 16 升级工作中,大部分精力都投入到了异步的 params 和 searchParams 改动上,因为这些改动会在各个地方引发严重问题。还有两项相对不那么显眼的路由改动同样需要重视:现在的并行路由槽需要一个明确的 default.js 文件,同时请求拦截功能正从 middleware.ts 转移到新的 proxy.ts 规范中。这两项改动都不会影响你日常访问的页面,正因如此才容易被忽视而未经过测试。本指南将解释这两项改动、它们为何迟迟未被发现,并提供升级前需执行的检查清单。
并行路由槽需要 default.js 文件
并行路由使用以 @ 开头的文件夹来定义布局需并排渲染的命名插槽。控制面板就是典型例子:主内容区域旁边是持续更新的动态信息流或通知面板,每个部分都有独立的导航状态。
在 Next.js 16 中,每个插槽都必须有 default.js 文件。当当前 URL 没有更具体的匹配项时,Next.js 会使用该文件来渲染插槽内容。如果没有这样的文件,导航到此类 URL 时会返回 404 错误,而无法实现平滑过渡。根据 Next.js 16 的升级说明,如果某个插槽缺少默认文件,构建过程可能会直接终止,因此请根据所使用的版本确认具体行为。带有信息流插槽的控制面板如下所示:
app/
dashboard/
@feed/
page.tsx ← the "active" state for this slot
default.js ← required fallback when no more-specific match exists
layout.tsx
page.tsx
回退方案通常无需做太多处理。渲染 null 即可隐藏该位置,而当需要保持面板可见时,则可使用空状态组件:
// app/dashboard/@feed/default.js
export default function Default() {
return null;
}
为何缺失的默认值会延迟出现
在开发过程中,人们主要在已构建和测试过的状态之间切换,这些状态在每个位置都能找到对应的内容。问题出现在用户遇到无人尝试过的组合时:深度链接、嵌套 URL 的页面刷新,或是仅存在于主内容中而不存在于信息流中的路由。由于完整加载页面时无法复用该位置的先前状态,Next.js 需要 default.js 来决定应渲染什么内容。实际上,这意味着在生产环境中往往是困惑的用户报告了 404 错误,而非在开发阶段就发现。即便认为某个位置永远不会被使用,为它添加对应的文件也是一种低成本的保障措施。
从 middleware.ts 到 proxy.ts
第二个变化涉及请求级别的拦截功能。Next.js 16 引入了 proxy.ts,作为处理原先位于 middleware.ts 中的逻辑的新标准,旧的文件名将逐渐被废弃。在当前版本中,迁移操作仅涉及重命名该文件及其导出的函数,且代理功能需要在 Node.js 运行时中执行;详情请参阅最新文档以及用于完成实际迁移操作的官方代码修改示例。
如果你的中间件功能很简单,那么迁移速度很快。但如果其中包含诸如身份验证、从子域名中解析租户信息、重定向或修改请求头等复杂功能,那就应将其视为需要审核的代码变更,而不仅仅是文件重命名而已。
逐个函数审查,而非仅重命名
直接从 middleware.ts 到 proxy.ts 进行盲目替换会假设所有逻辑在新规范下表现完全一致。实际上应逐项分析每个文件的真实功能,并分别验证其职责:
- 匹配器配置覆盖哪些请求,以及是否有路径被添加或删除
- 认证决策是如何做出的,当会话缺失或过期时会发生什么
- 如何从主机名或请求头中提取租户标识并传递给下游
- 哪些重定向和重写操作会被触发,以及执行顺序如何
- 哪些请求头会被读取、添加或删除
在这一层,细微的行为差异就会从外观问题演变为安全漏洞。遗漏的匹配项可能导致路由未经身份验证;更改的请求头则可能破坏租户隔离机制。正因如此,这一层需要经过仔细的审查和端到端测试,而非仅靠视觉上的快速检查。
两种无关的变更却出现相同的故障模式
并行路由默认设置与代理迁移在技术上毫无关联。各团队往往会在同一轮升级中发现这些问题,因为它们具有相同的模式:在开发环境中基本都能正常工作,但在某些难以复现的生产场景下就会出故障。
那种模式值得专门命名。那些会在每次页面加载时都出问题的变更会在几分钟内被发现。而那些有风险的变更则只在异常的导航路径或特定的请求格式下才会出现,因此常规的手动点击操作根本无法触发它们。你的测试计划应当有针对性地覆盖这些路径。
升级审计检查清单
在宣布 Next.js 16 升级完成之前,请核实以下内容:
- 每个并行路由槽都应包含一个
default.js文件。在app/目录中查找以@开头的文件夹,并逐一检查。 - 中间件逻辑已根据
proxy.ts的规范逐个责任项进行审查,而非直接重命名后就认为其功能相同。
为何多租户仪表板风险最高
SaaS仪表板通常同时具备这两种功能:用于展示面板的平行路由,以及在任何页面渲染之前将子域名映射到对应租户的中间件。这就使得租户识别功能直接处于最易受代理转换影响的代码中。
这里的错误很少会引发严重后果。你不会看到错误页面,只会收到有关某个租户短暂查看过另一租户数据的报告,这差不多就是SaaS产品可能出现的最严重问题了。多层防御机制能起到帮助作用:即便代理服务器已确定了租户身份,服务器组件和路由处理程序在访问数据时仍应再次验证该租户的所有权,而不可仅依赖上游设置的标头信息。基于标签的缓存也需要同样的谨慎处理,因为缓存键必须包含租户信息;关于Next.js 16的use cache及基于标签的重新验证功能的文章已对这方面进行了阐述。
常见问题
跳过default.js会导致无声失败吗?
并非完全如此。在遵循该规则的版本中,你会遇到404错误或构建错误,但仅出现在那些指向缺失回退内容的导航路径上。由于错误出现的位置远离其根源,除非了解这一变更,否则很容易被误认为是通用的路由问题。
目前还能继续使用middleware.ts吗?
请查阅与你所用具体小版本对应的Next.js文档,因为对旧规范的支持仍在不断演变中。鉴于认证功能经常存在于该文件中,无限期推迟迁移比推迟其他大多数升级任务风险更大,而尽早进行迁移则能给你足够的时间进行彻底审查。
如何快速找到没有default.js的插槽?
在 app/ 目录下搜索名称以 @ 开头的文件夹,并列出那些不包含 default.js(或 default.tsx)文件的文件夹。使用编辑器的文件搜索功能或简短的 shell 命令即可找到所有此类文件夹。
总结
Next.js 16 中最可能带来问题的变化并非那些会破坏正常运行流程的改动。没有默认值的并行路由槽以及草率重命名的代理在常规测试中可能通过,但在边缘情况下会出错,有时还会引发安全问题。应为每个路由槽添加默认值,逐项审查拦截逻辑的职责分配,并为这些路由安排充足的测试时间,因为常规流程测试往往无法覆盖它们。