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

