Next.js 15 中解决动态路由参数 await 错误
问题描述
在使用 Next.js 15 的应用路由(App Router)实现动态路由本地化时,访问路由参数 params.locale
会遇到以下错误:
bash
Error: Route "/[locale]" used `params.locale`. `params` should be awaited before using its properties.
Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
at locale (webpack:///app/[locale]/layout.tsx?a262:30:34)
典型项目结构:
bash
app/
├── [locale]/
│ ├── layout.tsx # 根布局文件
│ └── page.tsx # 主页面组件
locales/
├── en/
│ └── common.json
├── lt/
│ └── common.json
布局文件常包含以下形式的代码:
tsx
// app/[locale]/layout.tsx
export default async function RootLayout({
children,
params: { locale }, // 直接解构参数
}: {
children: React.ReactNode;
params: { locale: string };
}) {
return (
<html lang={locale}> // 错误触发位置
...
</html>
);
}
常见尝试解决方案:
- 将参数提取为局部变量
- 使用
generateStaticParams
预生成路由 - 添加参数类型声明
- 重启开发服务器
但上述方法均无法解决根本问题。
解决方案
正确 await 参数对象
Next.js 15 中,动态路由参数以异步 Promise 形式传递,必须显式 await:
tsx
// app/[locale]/layout.tsx - 修正后
export default async function RootLayout({
children,
params, // 不要直接解构!
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>; // 声明为Promise类型
}) {
const { locale } = await params; // 关键:await 参数
return (
<html lang={locale}> // 正确使用
...
</html>
);
}
同样适用于页面组件:
tsx
// app/[locale]/page.tsx
export default async function HomePage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params; // 正确获取参数
return (
<div>
...
</div>
);
}
重要提示
即使函数已声明为 async
,也必须显式 await params
检查静态文件位置
某些静态文件放置在动态路由目录下会意外触发此错误:
✅ 正确结构:
bash
app/
icon.png # 静态资源在根目录
[locale]/
layout.tsx
❌ 错误结构:
bash
app/
[locale]/
layout.tsx
icon.png # 静态资源触发错误
解决方法:将所有静态资源(favicon、图片等)移至 app/
根目录
Why?
Next.js 会将动态路由中的静态文件误识别为需动态处理的请求
使用代码修复工具
Next.js 官方提供自动修复工具:
bash
npx @next/codemod@canary next-async-request-api
此工具会自动检测并修复代码库中的 params
使用问题
API 路由的正确处理
对于 POST
、GET
等 API 路由,同样需 await params:
tsx
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params; // 解构前 await
return NextResponse.json({ id });
}
原理解析
Next.js 15 对动态路由参数处理机制进行了重要升级:
异步参数加载
- 路由参数现在异步解析
params
对象被包装在 Promise 中- 必须等待解析完成才能访问属性
静态文件冲突
- 放置于动态路由目录中的静态文件会被当作战利品收集行为
- 触发虚拟路由请求导致参数处理异常
官方迁移方案
next-async-request-api
codemod 专门处理此变更- 适配 Next.js 生态的异步演进要求
注意事项
- 仅针对 Next.js 15+ 版本有效
- Pages Router 不受此变更影响
- 开发环境可能需要清理缓存重新启动
最佳实践建议
统一参数处理
tsxinterface ParamsProps { params: Promise<{ locale: string // ...其他参数 }>; } export default async function Layout({ params }: ParamsProps) { const resolvedParams = await params; // 使用 resolvedParams.locale }
创建参数解析钩子
tsx// hooks/useRouteParams.ts export default async function useRouteParams( params: Promise<{ locale: string }> ) { return await params; } // layout.tsx import useRouteParams from '@/hooks/useRouteParams'; export default async function Layout({ params }) { const { locale } = await useRouteParams(params); }
文件结构规范
bashapp/ public/ favicon.ico # 推荐位置 app/ [locale]/ # 只包含需要动态处理的路由文件 assets/ # 静态资源目录 icons/
升级兼容性检查
json// package.json "dependencies": { "next": "^15.0.0", "react": "^19.0.0", "react-dom": "^19.0.0" }
遵循这些解决方案可彻底解决路由参数访问问题,同时确保代码符合 Next.js 15 的最新规范要求。