You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js13 App Router中结合Firebase使用generateStaticParams生成动态页面遇问题求助

解决Next.js 13 App Router中generateStaticParams无法生成参数的问题

核心问题定位

你的动态路由路径是/listing/[coffeeplace]/page.tsx,这意味着路由参数的键是coffeeplace,但你在代码里错误地使用了id作为参数名——不管是Page组件的params解构,还是generateStaticParams的返回值,都没和路由文件名对应,这就是params始终为undefined的根本原因。

分步解决方案

1. 修正Page组件的参数读取

修改Page组件的params类型定义和取值,确保和路由参数名一致:

export default async function Page({ params }: { params: { coffeeplace: string } }) {
  const { coffeeplace } = params; // 变量名必须和路由文件名[coffeeplace]对应

  // 用coffeeplace参数(即文档ID)获取数据
  const coffeeplaceData = fetchCoffeePlace(coffeeplace);
  const coffeeplaceInfoData = fetchCoffeePlace(coffeeplace);

  const coffeeplace = await coffeeplaceData;

  return (
    <div>
      <h1>{coffeeplace?.name}</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <CoffeeplaceInfo promise={coffeeplaceInfoData} />
      </Suspense>
    </div>
  );
}

2. 修正generateStaticParams的返回格式

generateStaticParams返回的每个对象的键必须和路由参数名coffeeplace完全匹配,不能用id:

export async function generateStaticParams() {
  const coffeePlaces = await getDocs(collection(db, "coffeePlaces"));

  // 用Firestore文档ID作为路由参数值
  return coffeePlaces.docs.map((doc) => ({
    coffeeplace: doc.id,
  }));
}

如果是硬编码测试,也要改成对应键名:

export async function generateStaticParams() {
  return [
    { coffeeplace: "9ZVOCYsngTksBKXqQQOH" },
    { coffeeplace: "r7CFv3t4Ni5sNl20wE8h" },
    { coffeeplace: "xndJfefMIYGjZSjKfWsC" },
  ];
}

3. 实现“店铺名称作为slug”的需求

如果想要URL显示店铺名称(如/listing/my-favorite-coffee-shop),同时用ID获取数据,有两种方案:

方案一:仅用slug作为路由参数
  • 先安装slugify工具(可选,也可自行实现简单转换):npm install slugify
  • 修改generateStaticParams生成slug,并将slug作为路由参数:
import slugify from 'slugify';

export async function generateStaticParams() {
  const coffeePlaces = await getDocs(collection(db, "coffeePlaces"));

  return coffeePlaces.docs.map((doc) => {
    const data = doc.data();
    // 生成唯一slug
    const slug = slugify(data.name, { lower: true, strict: true });
    return {
      slug: slug,
    };
  });
}
  • 修改fetchCoffeePlace函数,支持通过slug查询数据:
export async function fetchCoffeePlace(slug: string) {
  // 将slug转回名称进行查询
  const originalName = slug.replace(/-/g, " ");
  const q = query(collection(db, "coffeePlaces"), where("name", "==", originalName));
  const querySnapshot = await getDocs(q);
  if (!querySnapshot.empty) {
    return querySnapshot.docs[0].data() as CoffeePlace;
  }
  throw new Error("Coffee place not found");
}
方案二:同时包含ID和slug(更稳妥)

避免名称重复导致的冲突,路由改为/listing/[id]/[slug]/page.tsx:

  • generateStaticParams返回两个参数:
import slugify from 'slugify';

export async function generateStaticParams() {
  const coffeePlaces = await getDocs(collection(db, "coffeePlaces"));
  return coffeePlaces.docs.map((doc) => ({
    id: doc.id,
    slug: slugify(doc.data().name, { lower: true, strict: true }),
  }));
}
  • Page组件读取ID参数用于数据查询(slug仅优化URL美观):
export default async function Page({ params }: { params: { id: string, slug: string } }) {
  const { id } = params;
  const coffeeplaceData = fetchCoffeePlace(id);
  // ... 后续逻辑不变
}

4. 额外注意事项

  • Firestore文档的ID是doc.id,而非doc.data().id,除非你手动将ID存入了文档数据中,否则不要用后者获取ID。
  • 确保Firebase初始化逻辑在服务端环境中能正常运行,避免generateStaticParams执行时出现权限或连接问题。

内容的提问来源于stack exchange,提问作者Brt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 19:38:16