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

