React.js:如何从嵌套JSON动态创建DOM并渲染层级文档树菜单
我之前刚好处理过类似的嵌套JSON渲染层级文档树菜单的需求,根据不同的技术栈,分享几个实用的实现方案:
核心思路
你的JSON数据是典型的嵌套层级结构,每个节点包含title(显示文本)、path(跳转路径)、children(子节点数组)。核心逻辑就是递归遍历每个节点,根据节点是否有子节点来生成对应的层级DOM结构/组件。
方案1:原生JavaScript实现(无框架)
适合不需要前端框架的简单场景,直接用DOM操作生成菜单:
HTML结构
<div id="menu-container"></div>
JavaScript代码
// 修正了原数据里的路径错误(Ooty carrot的path对应Carrot而非Brinjal) const menuData = [ { "title": "Food", "path": "/root", "children": [ { "title": "Veg", "path": "/root/Food", "children": [ { "title": "Carrot", "path": "/root/Food/Veg", "children": [ { "title": "Ooty carrot", "path": "/root/Food/Veg/Carrot", "children": [] } ] } ] } ] }, { "title": "Cloths", "path": "/root", "children": [ { "title": "T shirt", "path": "/root/Cloths/Tshirt", "children": [] } ] } ]; // 递归生成菜单节点 function generateMenuNodes(data) { const ul = document.createElement('ul'); data.forEach(node => { const li = document.createElement('li'); // 创建跳转链接 const link = document.createElement('a'); link.href = node.path; link.textContent = node.title; li.appendChild(link); // 如果存在子节点,递归生成子菜单 if (node.children?.length) { li.appendChild(generateMenuNodes(node.children)); } ul.appendChild(li); }); return ul; } // 将菜单挂载到页面容器 const container = document.getElementById('menu-container'); container.appendChild(generateMenuNodes(menuData));
方案2:React 组件实现
如果你的项目用React,可以拆分为递归组件来实现:
import React from 'react'; // 单个菜单项组件,递归渲染子节点 const MenuItem = ({ node }) => { return ( <li> <a href={node.path}>{node.title}</a> {/* 存在子节点时渲染子菜单 */} {node.children?.length && ( <ul> {node.children.map((child, idx) => ( <MenuItem key={idx} node={child} /> ))} </ul> )} </li> ); }; // 菜单根组件 const DocumentTreeMenu = ({ data }) => { return ( <ul> {data.map((node, idx) => ( <MenuItem key={idx} node={node} /> ))} </ul> ); }; // 使用示例:在父组件中引入并传递数据 // <DocumentTreeMenu data={menuData} />
方案3:Vue 组件实现
Vue中可以利用递归组件的特性,实现起来非常简洁:
<template> <ul> <li v-for="(node, idx) in data" :key="idx"> <a :href="node.path">{{ node.title }}</a> <!-- 递归调用自身组件渲染子菜单 --> <menu-tree v-if="node.children?.length" :data="node.children" /> </li> </ul> </template> <script> export default { name: 'MenuTree', // 必须定义name才能递归调用 props: { data: { type: Array, required: true } } }; </script> <!-- 使用示例:在父组件中引入 --> <!-- <MenuTree :data="menuData" /> -->
额外优化建议
- 唯一Key的使用:示例中用了
idx作为key,实际项目建议用节点的唯一标识(比如新增id字段),避免列表更新时的渲染错误。 - 折叠展开功能:可以给有子节点的菜单添加折叠按钮,通过状态控制子菜单的显示/隐藏,提升交互体验。
- 样式美化:给不同层级的菜单添加缩进(比如
padding-left: 1.5rem)、图标等,让层级更清晰。
内容的提问来源于stack exchange,提问作者Siddhartha Chowdhury
相关产品推荐
相关产品推荐

