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

Next.js根级别动态路由实现:商品页与列表页路由问题

解决Next.js根级别商品详情页与列表页的路由冲突问题

方案1:在[slug].js中通过URL结构区分页面类型

你的商品详情页是单段URL(比如/xxx.html),列表页是两段URL(比如/women/pumps.html),可以直接在根级的[slug].js里解析路径段数量,分别渲染对应页面:

// pages/[slug].js
import { useRouter } from 'next/router';

export default function DynamicPage({ type, data }) {
  if (type === 'list') {
    // 渲染商品列表
    return (
      <div>
        <h1>{data.gender} 分类:{data.category}</h1>
        {/* 列表内容渲染 */}
      </div>
    );
  } else if (type === 'detail') {
    // 渲染商品详情
    return (
      <div>
        <h1>商品详情:{data.productName}</h1>
        {/* 详情内容渲染 */}
      </div>
    );
  }
  return <div>页面不存在</div>;
}

// 服务端提前判断并获取对应数据
export async function getServerSideProps(context) {
  const pathSegments = context.resolvedUrl.split('/').filter(Boolean);
  
  // 两段路径=列表页
  if (pathSegments.length === 2) {
    const [gender, categorySlug] = pathSegments;
    const category = categorySlug.replace('.html', '');
    // 调用API获取列表数据
    const products = await fetch(`你的API地址/${gender}/${category}`).then(res => res.json());
    
    return {
      props: {
        type: 'list',
        data: { gender, category, products }
      }
    };
  } 
  // 单段路径=详情页
  else if (pathSegments.length === 1) {
    const productSlug = context.query.slug.replace('.html', '');
    // 调用API获取详情数据
    const product = await fetch(`你的API地址/product/${productSlug}`).then(res => res.json());
    
    return {
      props: {
        type: 'detail',
        data: { productName: product.name, ...product }
      }
    };
  }

  // 异常路径返回404
  context.res.statusCode = 404;
  return { props: {} };
}

方案2:用Middleware做路由转发(Next.js 12+)

在根目录创建middleware.js,通过路径特征判断请求类型,转发到内部专属路由,不暴露外部URL结构:

// middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  const pathSegments = request.nextUrl.pathname.split('/').filter(Boolean);

  // 两段路径+结尾是.html=列表页,转发到内部_list路由
  if (pathSegments.length === 2 && pathSegments[1].endsWith('.html')) {
    const [gender, category] = pathSegments;
    const targetUrl = `/_list/${gender}/${category.replace('.html', '')}`;
    return NextResponse.rewrite(new URL(targetUrl, request.url));
  }
  // 单段路径+结尾是.html=详情页,转发到内部_detail路由
  else if (pathSegments.length === 1 && pathSegments[0].endsWith('.html')) {
    const slug = pathSegments[0].replace('.html', '');
    const targetUrl = `/_detail/${slug}`;
    return NextResponse.rewrite(new URL(targetUrl, request.url));
  }

  // 其他路径正常放行
  return NextResponse.next();
}

// 匹配所有根级别动态路径
export const config = {
  matcher: ['/:path*']
};

然后创建内部页面:

  • pages/_detail/[slug].js:专门处理商品详情的组件
  • pages/_list/[gender]/[category].js:专门处理商品列表的组件

用户访问外部URL时,会被Middleware透明转发到对应内部页面,完全不影响前端展示和SEO。

方案3:Next.js 13+ App Router 原生解决

如果用App Router,利用多段路由优先级高于单段路由的特性,配合next.config.js支持.html后缀:

  1. 配置next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      { source: '/:slug.html', destination: '/:slug' },
      { source: '/:gender/:category.html', destination: '/:gender/:category' },
    ];
  },
};

module.exports = nextConfig;
  1. 创建对应页面组件:
  • app/[slug]/page.js:商品详情页
export default function ProductDetail({ params }) {
  return <h1>商品详情:{params.slug}</h1>;
}
  • app/[gender]/[category]/page.js:商品列表页
export default function ProductList({ params }) {
  return <h1>{params.gender} 分类:{params.category}</h1>;
}

App Router会自动匹配路径段更多的路由,不会出现冲突。

内容的提问来源于stack exchange,提问作者H.Husain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 18:15:16