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>;
}
相关推荐
李白的手机6 分钟前
前端→Java→MySQL 时区时间处理全链路深度解析
前端·后端
踩着两条虫8 分钟前
AI驱动的 Vue3应用开发平台深入探究(十五):扩展与定制之自定义设置器与属性编辑器
前端·vue.js·人工智能·低代码·系统架构·编辑器
恋猫de小郭11 分钟前
Flutter 3.41.6 版本很重要,你大概率需要更新一下
android·前端·flutter
Surmon6 小时前
彻底搞懂大模型 Temperature、Top-p、Top-k 的区别!
前端·人工智能
木斯佳8 小时前
前端八股文面经大全:bilibili生态技术方向二面 (2026-03-25)·面经深度解析
前端·ai·ssd·sse·rag
不会写DN8 小时前
Gin 日志体系详解
前端·javascript·gin
冬夜戏雪9 小时前
实习面经记录(十)
java·前端·javascript
爱学习的程序媛10 小时前
【Web前端】JavaScript设计模式全解析
前端·javascript·设计模式·web
小码哥_常10 小时前
从SharedPreferences到DataStore:Android存储进化之路
前端
老黑10 小时前
开源工具 AIDA:给 AI 辅助开发加一个数据采集层,让 AI 从错误中自动学习(Glama 3A 认证)
前端·react.js·ai·nodejs·cursor·vibe coding·claude code