问题
Next.js 15 中,访问动态路由参数会报错:
bash
Error: Route "/work/[id]" used `params.id`. `params` should be awaited before using its properties.
这是一个破坏性变更 。Next.js 15 将动态路由参数 params
改为 Promise 形式,目的是为了:
- 提高性能,支持流式渲染
- 统一异步数据处理模式
- 为未来的并发特性做准备
两种解决方法
什么是服务器组件和客户端组件?
在 Next.js 中:
- 服务器组件:在服务器上运行,可以直接访问数据库,不能使用浏览器 API(默认模式)
- 客户端组件 :在浏览器中运行,可以使用点击事件、状态等交互功能(需要添加
'use client'
)
方法一:async/await(服务器组件用)
适用于 :普通页面、布局文件(没有 'use client'
的组件)
tsx
// app/work/[id]/page.tsx
export default async function WorkPage({
params
}: {
params: Promise<{ id: string }>
}) {
// 等待获取参数
const { id } = await params;
return (
<div>
<h1>工作项目 {id}</h1>
</div>
);
}
tsx
// app/work/[id]/layout.tsx
export default async function Layout({
params,
children,
}: {
params: Promise<{ id: string }>;
children: React.ReactNode;
}) {
const { id } = await params;
return (
<div>
<aside>侧边栏 - 项目 {id}</aside>
<main>{children}</main>
</div>
);
}
方法二:use() 函数(客户端组件用)
适用于 :有交互功能的组件(带 'use client'
的组件)
tsx
// app/work/[id]/components/WorkButton.tsx
'use client'; // 客户端组件标记
import { use } from 'react';
export default function WorkButton({
params
}: {
params: Promise<{ id: string }>;
}) {
// 使用 use() 获取参数
const { id } = use(params);
const handleClick = () => {
alert(`编辑项目 ${id}`);
};
return (
<button onClick={handleClick}>
编辑项目 {id}
</button>
);
}
完整示例
页面文件(服务器组件)
tsx
// app/work/[id]/page.tsx
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return (
<div className="p-8">
<h1>项目详情 {id}</h1>
<p>这是项目 {id} 的内容</p>
</div>
);
}
布局文件(服务器组件)
tsx
// app/work/[id]/layout.tsx
export default async function Layout({
params,
children,
}: {
params: Promise<{ id: string }>;
children: React.ReactNode;
}) {
const { id } = await params;
return (
<div className="flex">
<nav className="w-64 p-4">
<h2>项目 {id} 导航</h2>
</nav>
<div className="flex-1">
{children}
</div>
</div>
);
}
交互组件(客户端组件)
tsx
// app/work/[id]/components/ProjectActions.tsx
'use client';
import { use, useState } from 'react';
export default function ProjectActions({
params
}: {
params: Promise<{ id: string }>;
}) {
const { id } = use(params);
const [liked, setLiked] = useState(false);
return (
<div>
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'} 项目 {id}
</button>
<button onClick={() => alert(`分享项目 ${id}`)}>
分享项目 {id}
</button>
</div>
);
}
如何选择方法
组件类型 | 特征 | 使用方法 |
---|---|---|
服务器组件 | - 没有 'use client' - 不能有点击事件 - 页面、布局文件 |
async/await |
客户端组件 | - 有 'use client' - 有点击、输入等交互 - 使用 useState |
use() 函数 |
修改步骤
- 找到所有使用
params
的文件 - 判断是服务器组件还是客户端组件
- 修改代码:
服务器组件:
tsx
// 修改前
export default function Page({ params }) {
return <div>{params.id}</div>;
}
// 修改后
export default async function Page({ params }) {
const { id } = await params;
return <div>{id}</div>;
}
客户端组件:
tsx
// 修改前
'use client';
export default function Component({ params }) {
return <div>{params.id}</div>;
}
// 修改后
'use client';
import { use } from 'react';
export default function Component({ params }) {
const { id } = use(params);
return <div>{id}</div>;
}
常见错误
❌ 错误写法
tsx
// 服务器组件中使用 use()
export default function Page({ params }) {
const { id } = use(params); // 错误!服务器组件应该用 async/await
return <div>{id}</div>;
}
// 客户端组件中使用 async/await
'use client';
export default async function Button({ params }) { // 错误!客户端组件不能是 async
const { id } = await params;
return <button>{id}</button>;
}
✅ 正确写法
tsx
// 服务器组件用 async/await
export default async function Page({ params }) {
const { id } = await params;
return <div>{id}</div>;
}
// 客户端组件用 use()
'use client';
import { use } from 'react';
export default function Button({ params }) {
const { id } = use(params);
return <button>{id}</button>;
}