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 分钟前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞057 分钟前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、12 分钟前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao12 分钟前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly19 分钟前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强
hedley(●'◡'●)1 小时前
基于cesium和vue的大疆司空模仿程序
前端·javascript·vue.js·python·typescript·无人机
qq5_8115175151 小时前
web城乡居民基本医疗信息管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
百思可瑞教育1 小时前
构建自己的Vue UI组件库:从设计到发布
前端·javascript·vue.js·ui·百思可瑞教育·北京百思教育
百锦再1 小时前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
CappuccinoRose1 小时前
JavaScript 学习文档(二)
前端·javascript·学习·数据类型·运算符·箭头函数·变量声明