NextJS 14可选Catch-all Segments动态路由:前置可选slug配置失效求助
实现可选路由段前置、固定段后置的Next.js路由
Next.js的文件系统路由规则明确要求:catch-all(包括可选catch-all)必须作为路由的最后一段,所以你之前尝试的目录结构会触发错误。要实现/category1?/category2?/category3?/category4?/c/categoryId的URL模式,可以通过路由重写的方式实现,以下是两种可行方案:
方案一:App Router 用 Middleware 重写路由
步骤1:创建路由中间件
在项目根目录或app目录下创建middleware.ts,匹配目标URL格式并进行重写:
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const url = request.nextUrl; // 匹配最多4个前置可选段的URL格式 const pathMatch = url.pathname.match(/^\/(([^/]+\/){0,3}[^/]+)?\/c\/([^/]+)$/); if (pathMatch) { // 提取前置分类段和categoryId const rawCategories = pathMatch[1]?.split('/').filter(Boolean) || []; const categoryId = pathMatch[3]; // 限制前置段最多4个,超出则返回404 if (rawCategories.length > 4) { return new NextResponse('Invalid URL', { status: 404 }); } // 重写到内部路由 /c/[categoryId]/[[...categories]] return NextResponse.rewrite( new URL(`/c/${categoryId}/${rawCategories.join('/')}`, request.url) ); } return NextResponse.next(); } // 指定中间件作用范围(排除静态资源和API) export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
步骤2:创建内部处理路由
按照以下目录结构创建页面文件:
- app - c - [categoryId] - [[...categories]] - page.tsx
步骤3:在页面中处理参数
在page.tsx中获取并使用路由参数:
export default function CategoryPage({ params, }: { params: { categoryId: string; categories?: string[]; }; }) { const categories = params.categories || []; return ( <div> <h2>分类ID:{params.categoryId}</h2> <p>前置分类层级:{categories.length > 0 ? categories.join(' > ') : '无'}</p> </div> ); }
方案二:Page Router 用 next.config.js 配置重写
如果你使用的是Page Router,可以通过next.config.js的rewrites配置实现相同效果:
步骤1:配置重写规则
修改next.config.js:
/** @type {import('next').NextConfig} */ const nextConfig = { async rewrites() { return [ { // 匹配带前置可选段的URL source: '/:categories*/c/:categoryId', // 重写到内部路由 destination: '/c/:categoryId/:categories*', }, ]; }, }; module.exports = nextConfig;
步骤2:创建处理页面
在pages目录下创建路由文件:
- pages - c - [categoryId] - [[...categories]].tsx
步骤3:页面中处理参数
在[[...categories]].tsx中获取参数:
import { useRouter } from 'next/router'; export default function CategoryPage() { const router = useRouter(); const { categoryId, categories } = router.query; const categoryList = Array.isArray(categories) ? categories : []; // 限制前置段最多4个 if (categoryList.length > 4) { return <div>无效的URL格式</div>; } return ( <div> <h2>分类ID:{categoryId}</h2> <p>前置分类层级:{categoryList.join(' > ') || '无'}</p> </div> ); }
两种方案都能让用户访问/a/c/123、/a/b/c/456、/a/b/c/d/789这类符合要求的URL,同时内部通过重写路由来处理参数,完美适配你的需求。
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

