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>;
}
相关推荐
Filotimo_4 小时前
2.CSS3.(2).html
前端·css
yinuo5 小时前
uniapp微信小程序华为鸿蒙定时器熄屏停止
前端
gnip6 小时前
vite中自动根据约定目录生成路由配置
前端·javascript
前端橙一陈7 小时前
LocalStorage Token vs HttpOnly Cookie 认证方案
前端·spring boot
~无忧花开~7 小时前
JavaScript学习笔记(十五):ES6模板字符串使用指南
开发语言·前端·javascript·vue.js·学习·es6·js
泰迪智能科技017 小时前
图书推荐丨Web数据可视化(ECharts 5)(微课版)
前端·信息可视化·echarts
CodeCraft Studio8 小时前
借助Aspose.Email,使用 Python 读取 Outlook MSG 文件
前端·python·outlook·aspose·email·msg·python读取msg文件
家里有只小肥猫9 小时前
react 初体验2
前端·react.js·前端框架
慧慧吖@9 小时前
前端发送请求时,参数的传递格式
前端
L李什么李9 小时前
HTML——使用表格制作简历
前端·javascript·html