如何遍历JSON子树以构建完整层级导航菜单?
解决菜单子树的完整层级路径遍历问题
首先先修正一下你提供的原始JSON结构,里面多了一层冗余的大括号,正确的格式应该是这样(避免后续解析出错):
[ { "link": { "title": "Applications" }, "has_children": true, "subtree": [ { "link": { "title": "Projects" }, "subtree": [ { "link": { "title": "WORK/NEW YORK" } } ] } ] } ]
你遇到的问题很典型:嵌套循环只能处理固定层级的菜单,一旦菜单深度变化就失效;而之前尝试的递归因为和DOM操作绑定变得复杂。其实我们可以把路径收集和DOM操作完全分开,用递归或者迭代的方式专门处理路径遍历,这样就会简洁很多。
下面给你两种实用的方案:
方案一:递归遍历(专注路径收集,不碰DOM)
这个方法的核心是维护当前遍历的路径,遇到叶子节点(没有子节点)时,就把完整路径保存下来。完全不需要操作DOM,逻辑非常清晰:
const menuData = [ { "link": { "title": "Applications" }, "has_children": true, "subtree": [ { "link": { "title": "Projects" }, "subtree": [ { "link": { "title": "WORK/NEW YORK" } } ] } ] } ]; function collectFullPaths(nodes, currentPath = [], result = []) { nodes.forEach(node => { // 将当前节点标题加入路径 const updatedPath = [...currentPath, node.link.title]; // 判断是否有子节点(用subtree是否存在且非空比has_children更可靠) if (node.subtree && node.subtree.length > 0) { // 递归处理子节点,传递更新后的路径 collectFullPaths(node.subtree, updatedPath, result); } else { // 没有子节点,把完整路径拼接成你要的格式,加入结果数组 result.push(updatedPath.join(' => ')); } }); return result; } // 调用示例 const allPaths = collectFullPaths(menuData); console.log(allPaths); // 输出: ["Applications => Projects => WORK/NEW YORK"]
你可以拿到allPaths之后,再用它去做后续的DOM操作,这样逻辑拆分后,递归部分就完全不复杂了。
方案二:迭代式深度优先遍历(避免递归调用栈)
如果担心递归的调用栈问题(比如菜单层级极深),可以用栈来模拟递归过程,同样专注于路径收集:
function collectPathsIteratively(nodes) { const result = []; // 栈中存储的是 [当前要处理的节点数组, 当前已遍历的路径] const stack = [[nodes, []]]; while (stack.length > 0) { const [currentNodes, currentPath] = stack.pop(); currentNodes.forEach(node => { const updatedPath = [...currentPath, node.link.title]; if (node.subtree && node.subtree.length > 0) { // 把子节点和更新后的路径压入栈 stack.push([node.subtree, updatedPath]); } else { result.push(updatedPath.join(' => ')); } }); } // 因为栈是后进先出,最后反转结果保持原顺序 return result.reverse(); } // 调用示例 const iterativePaths = collectPathsIteratively(menuData); console.log(iterativePaths); // 输出: ["Applications => Projects => WORK/NEW YORK"]
为什么这两种方案适合你?
- 不管菜单有多少层级,都能自动适配,不像嵌套循环只能处理固定深度;
- 完全把路径收集和DOM操作解耦,你可以先拿到所有完整路径,再去执行
buildingMenuContent之类的DOM操作,逻辑更清晰; - 代码简洁,没有多余的DOM操作干扰,维护起来很方便。
内容的提问来源于stack exchange,提问作者Zyzz Shembesh
相关产品推荐
相关产品推荐

