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

Next.js客户端API渲染数据异常 嵌套路由实现问题咨询

问题根因

所有页面渲染结果完全一致是几个明显的代码错误叠加导致的:

  • getStaticProps参数传递错误:分类页的getStaticProps中,请求分类API拿到的过滤后结果存在const item变量中,但返回props时传入的是外层getStaticPaths作用域里拉取的全量文章items,等于不管访问哪个分类路径,传给前端组件的始终是全量数据,渲染结果自然完全相同。
  • API路由结构与逻辑不符合预期
    • 你把category和subcategory建成了平级的API目录,根本无法生成/category/[category]/subcategory/[subcategory]的嵌套路径
    • 所有API处理函数只写了category匹配逻辑,完全没有实现subcategory的过滤规则
    • 过滤结果只取了filtered[0]单条数据,没有返回匹配分类的所有条目
    • 动态路由文件命名为[slug].js,但函数内解构的参数是category,参数名和动态段不匹配时根本拿不到路径里的动态值
  • Pages路由结构错误:和API路由问题一致,subcategory文件夹和category文件夹平级放置,最终生成的路径是/subcategory/[subcategory],不是嵌套在分类下的目标路径。

最优实现方案

1. 精简重构API路由

删除所有冗余的零散路由文件,直接用Next.js嵌套动态路由能力实现需求,最终API目录结构如下:

pages/api
└── category
    └── [category]
        ├── index.js  // 匹配 /api/category/[category],返回指定分类下所有文章
        └── subcategory
            └── [subcategory].js // 匹配 /api/category/[category]/subcategory/[subcategory],返回指定分类+子分类下的所有文章
  • 单分类接口(pages/api/category/[category]/index.js)代码:
import { items } from '../../../../data'

export default function handler({ query: { category } }, res) {
  const filtered = items.filter((item) => item.category === category)
  // 返回所有匹配条目,不要只取第一条
  res.status(200).json(filtered)
}
  • 分类+子分类嵌套接口(pages/api/category/[category]/subcategory/[subcategory].js)代码:
import { items } from '../../../../../../data'

export default function handler({ query: { category, subcategory } }, res) {
  // 同时匹配分类和子分类两个条件
  const filtered = items.filter(
    (item) => item.category === category && item.subcategory === subcategory
  )
  res.status(200).json(filtered)
}

之前平级创建的subcategory API文件夹、冗余的category根index.js、参数不匹配的[slug].js都可以直接删除。

2. 重构Pages前端路由

对应目标路径,pages目录采用嵌套结构,删除原来平级的subcategory文件夹,最终结构如下:

pages/category
└── [category]
    ├── index.js // 匹配 /category/[category] 分类列表页
    └── subcategory
        └── [subcategory].js // 匹配 /category/[category]/subcategory/[subcategory] 子分类列表页
  • 分类列表页(pages/category/[category]/index.js)修正后代码:
import Link from 'next/link'
import Meta from '../../../components/Meta' // 替换为你项目里的实际组件路径
import ItemList from '../../../components/ItemList' // 替换为你项目里的实际组件路径
import { server } from '../../../config' // 替换为你项目里server常量的实际路径

const CategoryPage = ({ items }) => {
  return (
    <>
      <Meta 
        title={items[0]?.category || '文章分类'} 
        description={items[0]?.category || '文章分类'} 
      />
      <div>
        <ItemList items={items}/>
      </div>
      <Link href='/'>返回首页</Link>
    </>
  )
}

export const getStaticProps = async (context) => {
  const res = await fetch(`${server}/api/category/${context.params.category}`)
  // 注意变量名不要和外层作用域冲突,返回的props要和接口返回值对应
  const items = await res.json()

  return {
    props: {
      items,
    },
  }
}

export const getStaticPaths = async () => {
  const res = await fetch(`${server}/api/items`)
  const allItems = await res.json()
  // 对分类去重,避免重复生成同一路由
  const categories = [...new Set(allItems.map((item) => item.category))]
  const paths = categories.map((category) => ({ params: { category } }))

  return {
    paths,
    fallback: false,
  }
}

export default CategoryPage
  • 子分类列表页(pages/category/[category]/subcategory/[subcategory].js)代码:
import Link from 'next/link'
import Meta from '../../../../../components/Meta' // 替换为你项目里的实际组件路径
import ItemList from '../../../../../components/ItemList' // 替换为你项目里的实际组件路径
import { server } from '../../../../../config' // 替换为你项目里server常量的实际路径

const SubcategoryPage = ({ items }) => {
  return (
    <>
      <Meta 
        title={items[0]?.subcategory || '子分类'} 
        description={items[0]?.subcategory || '子分类'} 
      />
      <div>
        <ItemList items={items}/>
      </div>
      <Link href={`/category/${items[0]?.category}`}>返回上级分类</Link>
    </>
  )
}

export const getStaticProps = async (context) => {
  const { category, subcategory } = context.params
  const res = await fetch(`${server}/api/category/${category}/subcategory/${subcategory}`)
  const items = await res.json()

  return {
    props: {
      items,
    },
  }
}

export const getStaticPaths = async () => {
  const res = await fetch(`${server}/api/items`)
  const allItems = await res.json()
  // 对分类+子分类的组合去重,生成所有合法路径
  const pathSet = new Set()
  allItems.forEach(item => {
    pathSet.add(`${item.category}|||${item.subcategory}`)
  })
  const paths = Array.from(pathSet).map(str => {
    const [category, subcategory] = str.split('|||')
    return { params: { category, subcategory } }
  })

  return {
    paths,
    fallback: false,
  }
}

export default SubcategoryPage

额外优化提示

如果你的items数据是项目内本地存储的静态文件,完全不需要在getStaticProps中请求自身API路由,直接在服务端代码中引入data文件做过滤即可,可以省掉不必要的请求开销,提升构建速度。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:27:14