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

Next.js三级动态路由实现遇阻,求技术解决方案

Next.js 三级动态路由问题修复方案

核心问题分析

当前代码的主要问题集中在路由文件结构不匹配、getStaticPaths参数缺失/格式错误,以及getStaticProps数据筛选逻辑不完善这几点:

  • 三级路由需要对应页面路径:pages/blog/[category]/[subcategory]/[post].js
  • 子分类页面的getStaticPaths返回二维数组,需扁平化;参数引用存在错误
  • 文章页面未在getStaticPaths中提供subcategory参数,无法匹配三级路由
  • 文章数据筛选未关联分类和子分类,可能导致数据匹配错误

1. 调整页面文件结构

确保页面目录完全匹配三级路由规则:

pages/
└── blog/
    ├── [category]/
    │   ├── index.js          # 二级分类页面(现有)
    │   ├── [post].js         # 二级文章页面(现有)
    │   └── [subcategory]/
    │       ├── index.js      # 子分类页面(现有)
    │       └── [post].js     # 三级文章页面(新增)

2. 修正分类页面(pages/blog/[category]/index.js)

确保子分类链接指向正确路径,文章链接保留二级路由逻辑:

import Link from "next/link";
import { getCategories, getPosts } from "../../../utils/datocmsHelper";

export default function CategoriesPage({ category, subcategories, posts }) {
  return (
    <div>
      <h1>{category.categoryName}</h1>
      <h2>当前分类下的子分类:</h2>
      <ul>
        {subcategories.map((subcategory) => (
          <li key={subcategory.slug}>
            <Link href={`/blog/${category.slug}/${subcategory.slug}`}>
              {subcategory.slug}
            </Link>
          </li>
        ))}
      </ul>
      <h2>当前分类下的文章:</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <Link href={`/blog/${category.slug}/${post.slug}`}>
              {post.slug}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

export async function getStaticProps(ctx) {
  const categoriesData = await getCategories();
  const postsData = await getPosts();

  const currentCategory = categoriesData.allCategories.find(
    (category) => category.slug === ctx.params.category
  );

  // 筛选当前分类下的子分类
  const subcategoriesInCategory = categoriesData.allCategories.filter(
    (subcategory) => subcategory.category?.slug === currentCategory.slug
  );

  // 筛选当前分类下的文章(不含子分类文章)
  const postsInCategory = postsData.allPosts.filter(
    (post) => post.category.slug === currentCategory.slug && !post.subcategory
  );

  return {
    props: {
      category: currentCategory,
      subcategories: subcategoriesInCategory,
      posts: postsInCategory,
    },
  };
}

export async function getStaticPaths() {
  const categoriesData = await getCategories();

  return {
    paths: categoriesData.allCategories
      .filter((category) => !category.category) // 仅生成一级分类路由
      .map((category) => ({
        params: { category: category.slug },
      })),
    fallback: false,
  };
}

3. 修正子分类页面(pages/blog/[category]/[subcategory]/index.js)

修复getStaticPaths的数组扁平化问题,补充文章列表链接:

import Link from "next/link";
import { getPosts, getCategories } from "../../../../utils/datocmsHelper";

export default function SubcategoryPage({ subcategory, posts }) {
  return (
    <div className="flex flex-col">
      <h1>子分类: {subcategory.slug}</h1>
      <p>所属分类: {subcategory.category.slug}</p>
      <h2>当前子分类下的文章:</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <Link href={`/blog/${subcategory.category.slug}/${subcategory.slug}/${post.slug}`}>
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

export async function getStaticProps(ctx) {
  const categoriesData = await getCategories();
  const postsData = await getPosts();

  // 根据分类和子分类参数精准匹配当前子分类
  const currentSubcategory = categoriesData.allCategories.find(
    (subcat) => 
      subcat.slug === ctx.params.subcategory && 
      subcat.category.slug === ctx.params.category
  );

  // 筛选当前子分类下的文章
  const postsInSubcategory = postsData.allPosts.filter(
    (post) => post.subcategory?.slug === currentSubcategory.slug
  );

  return {
    props: {
      subcategory: currentSubcategory,
      posts: postsInSubcategory,
    },
  };
}

export async function getStaticPaths() {
  const categoriesData = await getCategories();

  // 扁平化数组,生成正确的分类+子分类路由组合
  const paths = categoriesData.allCategories
    .filter((category) => !category.category) // 一级分类
    .flatMap((category) => 
      category.children.map((subcategory) => ({
        params: {
          category: category.slug,
          subcategory: subcategory.slug,
        },
      }))
    );

  return {
    paths,
    fallback: false,
  };
}

4. 新增三级文章页面(pages/blog/[category]/[subcategory]/[post].js)

补充subcategory参数,完善文章匹配逻辑:

import { getPosts } from "../../../../../utils/datocmsHelper";

export default function PostPage({ post }) {
  return (
    <div className="flex flex-col">
      <h1>文章标题: {post.title}</h1>
      <p>所属分类: {post.category.slug}</p>
      <p>所属子分类: {post.subcategory.slug}</p>
      <div>文章内容: {post.content}</div>
    </div>
  );
}

export async function getStaticProps(ctx) {
  const postsData = await getPosts();

  // 根据分类、子分类、文章slug精准匹配
  const currentPost = postsData.allPosts.find(
    (post) => 
      post.slug === ctx.params.post && 
      post.category.slug === ctx.params.category && 
      post.subcategory?.slug === ctx.params.subcategory
  );

  return {
    props: {
      post: currentPost,
    },
  };
}

export async function getStaticPaths() {
  const postsData = await getPosts();

  // 仅生成属于子分类的文章路由
  const paths = postsData.allPosts
    .filter((post) => post.subcategory)
    .map((post) => ({
      params: {
        category: post.category.slug,
        subcategory: post.subcategory.slug,
        post: post.slug,
      },
    }));

  return {
    paths,
    fallback: false,
  };
}

关键注意事项

  • 确保DatoCMS中的文章数据包含subcategory关联字段,且getPosts的GraphQL查询已获取该字段
  • 若需支持同一文章同时出现在二级和三级路由,需调整getStaticPaths和筛选逻辑,避免路由冲突
  • 若使用Next.js 13+ App Router,路由结构和数据获取逻辑需对应调整

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:30:45