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
相关产品推荐
相关产品推荐

