Next.js13电商站点动态路由问题:单路径匹配分类/集合slug
解决Next.js App Router中分类/集合共享slug路径的404问题
你的核心问题是同一个/products/{slug}路径会同时触发路由组里的两个page.tsx,其中必然有一个会因为找不到对应数据抛出notFound(),导致页面报错。下面是直接可行的解决方案:
1. 调整路由结构
删除(collections)和(categories)两个路由组,合并成单个page.tsx,最终结构如下:
products / [slug] / layout.tsx page.tsx
2. 统一处理slug匹配逻辑
在新的page.tsx中,先尝试匹配分类数据,失败后再匹配集合数据,两者都匹配失败才抛出notFound():
import { notFound } from 'next/navigation'; import ProductList from '@/components/ProductList'; import FilterSidebar from '@/components/FilterSidebar'; // 封装统一的slug查询函数 async function resolveSlug(slug: string) { // 优先查询分类 const category = await getCategoryProducts(slug); if (category) return { type: 'category', data: category }; // 分类不存在则查询集合 const collection = await getCollectionProducts(slug); if (collection) return { type: 'collection', data: collection }; // 两者都不存在返回null return null; } export default async function ProductPage({ params }: { params: { slug: string } }) { const resolvedData = await resolveSlug(params.slug); if (!resolvedData) { notFound(); } return ( <div className="product-page"> <h1> {resolvedData.type === 'category' ? `分类:${resolvedData.data.name}` : `集合:${resolvedData.data.name}`} </h1> {/* 渲染商品列表 */} <ProductList products={resolvedData.data.products} /> {/* 渲染全局筛选组件(包含所有分类和集合选项) */} <FilterSidebar /> </div> ); }
3. 处理静态生成(generateStaticParams)
如果需要静态生成页面,并行获取所有分类和集合的slug,合并后返回即可:
export async function generateStaticParams() { // 并行请求分类和集合的slug列表 const [categorySlugs, collectionSlugs] = await Promise.all([ getAllCategorySlugs(), // 需返回形如 [{ slug: 'electronics' }, ...] 的数组 getAllCollectionSlugs() // 需返回形如 [{ slug: 'summer-sale' }, ...] 的数组 ]); // 合并并去重(如果后端保证slug唯一可省去去重步骤) const uniqueSlugs = Array.from( new Set([...categorySlugs, ...collectionSlugs].map(item => item.slug)) ).map(slug => ({ slug })); return uniqueSlugs; }
注意事项
- 确保后端分类和集合的slug不重复,否则会出现优先级问题(先查询到的类型会被返回)。
- 可以在
layout.tsx中渲染公共的筛选栏组件,展示所有可用的分类和集合选项,无需在每个子页面重复实现。
内容的提问来源于stack exchange,提问作者Jonathan Franzen
相关产品推荐
相关产品推荐

