my-first-ai-web_问题记录02:Next.js 15 动态路由参数处理

问题

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() 函数

修改步骤

  1. 找到所有使用 params 的文件
  2. 判断是服务器组件还是客户端组件
  3. 修改代码

服务器组件

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>;
}
相关推荐
有味道的男人9 分钟前
如何使用招标网API获取项目详情?
java·服务器·前端
qq_4061761410 分钟前
深入剖析JS中的XSS与CSRF漏洞:原理、攻击与防御全指南
服务器·开发语言·前端·javascript
RFCEO11 分钟前
HTML编程 课程六、:HTML5 新增多媒体标签
前端·html·html5·多媒体标签·嵌入音频、视频、动画
yanyu-yaya19 分钟前
速学兼复习之vue3章节4
前端·vue.js·前端框架
Mr-Wanter23 分钟前
vue 数据反显时数字/字母不换行导致的样式问题
前端·javascript·vue.js
梁萌30 分钟前
vue项目从npm升级为pnpm
前端·npm·node.js
修己xj31 分钟前
CSS魔法:对话生成器与奔驰骏马的创意实现
前端·css
琹箐1 小时前
Cursor 无法使用prettier格式化
前端
觉醒大王1 小时前
如何整理文献阅读笔记? (精读与泛读)
前端·css·笔记·深度学习·自然语言处理·html·学习方法
广州华水科技1 小时前
单北斗GNSS变形监测系统在水库安全监测中的应用与发展
前端