JavaScript递归生成页面面包屑扁平数组的问题修复
问题背景
我正在通过Contentful API处理数据,搭建目标网站的结构。目前使用GraphQL API获取Contentful页面数据,但受API速率限制,查询引用关系和层级结构时无法设置过深的查询层级。
因此我选择查询pages内容类型,仅获取每个页面及其上一级父页面ID,计划在构建阶段(使用Eleventy构建站点)通过JavaScript递归逻辑拼接出完整的页面层级结构。
我需要一个可生成面包屑数组的函数:理想状态下传入单个页面ID,函数即可遍历数据集,依次获取父页面、父页面的父页面详情,直到无上级父页面为止。
当前实现
我当前编写了两个拆分函数,不确定是否为最优实现:
/** * 接收页面ID,在contentfulPages数组(Contentful GraphQL查询返回结果)中查找对应页面 * 返回包含页面ID、slug(用于生成URL)、title(用于展示可读文本)、parentPageId的对象,供后续获取父页面信息使用 */ function getParentPage(pageId) { const { id, slug, title, parentPageId } = contentfulPages.find( (page) => page.id === pageId ); return { id, slug, title, parentPageId }; } /** * 接收页面ID,调用getParentPage获取第一层父页面详情。如果存在更高层级父页面, * 则使用新找到的父页面ID递归调用自身,将返回对象推入breadcrumbArray。 * 由于函数直接返回面包屑数组,当前实现每次递归都会生成一层嵌套数组,而非同层级对象 */ function getBreadcrumb(pageId) { let breadcrumbArray = []; const { id, slug, title, parentPageId } = getParentPage(pageId); breadcrumbArray.push({ id, slug, title, parentPageId }); if (parentPageId) { breadcrumbArray.push(getBreadcrumb(parentPageId)); } return breadcrumbArray; }
问题描述
当前代码返回多层嵌套数组,预期输出为由页面对象组成的扁平数组,三层深度页面的预期输出与实际输出对比如下:
// 预期输出 [ { id: 'page-3', slug: 'courses', title: 'Courses', parentPageId: 'page-2' }, { id: 'page-2', slug: 'training', title: 'Training', parentPageId: 'page-1' }, { id: 'page-1', slug: 'services', title: 'Services', parentPageId: false }, ] // 实际输出 [ { id: 'page-3', slug: 'courses', title: 'Courses', parentPageId: 'page-2' }, [ { id: 'page-2', slug: 'training', title: 'Training', parentPageId: 'page-1' }, [ { id: 'page-1', slug: 'services', title: 'Services', parentPageId: false }, ] ] ]
修复方案
嵌套问题的根源是递归调用getBreadcrumb的返回值本身是数组,直接用push插入会将整个数组作为单个元素存入当前数组,最终形成多层嵌套结构。使用展开运算符将递归返回的数组元素逐个合并到当前数组,即可得到扁平结构。
修改后的getBreadcrumb函数如下:
function getBreadcrumb(pageId) { const currentPage = getParentPage(pageId); // 无上级父页面时终止递归,返回仅包含当前页面的数组 if (!currentPage.parentPageId) { return [currentPage]; } // 合并当前页面与父级面包屑结果,保持当前页在前、根页在后的顺序 return [currentPage, ...getBreadcrumb(currentPage.parentPageId)]; }
补充:常规面包屑导航需要按「根页面 -> 子页面 -> 当前页」的顺序展示,只需要调整返回顺序为
return [...getBreadcrumb(currentPage.parentPageId), currentPage]即可。
建议给getParentPage增加容错逻辑,避免传入不存在的页面ID时解构报错:
function getParentPage(pageId) { const targetPage = contentfulPages.find(page => page.id === pageId); // 找不到对应页面时返回默认值,可根据业务需求调整为抛出明确错误 if (!targetPage) { return { id: null, slug: '', title: '', parentPageId: false }; } const { id, slug, title, parentPageId } = targetPage; return { id, slug, title, parentPageId }; }
内容的提问来源于stack exchange,提问作者user1765924
相关产品推荐
相关产品推荐

