js实现无穷树形菜单
实现思路
使用递归组件实现无穷树形菜单,核心在于组件能够调用自身,并动态渲染子节点。数据结构通常采用嵌套的children数组形式。
数据结构示例
const treeData = [
{
id: 1,
label: '节点1',
children: [
{
id: 2,
label: '节点1.1',
children: [
{ id: 3, label: '节点1.1.1' }
]
}
]
},
{
id: 4,
label: '节点2'
}
]
递归组件实现
function TreeNode({ node }) {
const [isExpanded, setIsExpanded] = useState(false)
return (
<div className="tree-node">
<div
className="node-label"
onClick={() => setIsExpanded(!isExpanded)}
>
{node.children && (
<span className="toggle-icon">
{isExpanded ? '−' : '+'}
</span>
)}
{node.label}
</div>
{isExpanded && node.children && (
<div className="children">
{node.children.map(child => (
<TreeNode key={child.id} node={child} />
))}
</div>
)}
</div>
)
}
// 使用方式
function TreeMenu() {
return (
<div className="tree-menu">
{treeData.map(node => (
<TreeNode key={node.id} node={node} />
))}
</div>
)
}
样式建议
.tree-node {
margin-left: 20px;
cursor: pointer;
}
.node-label {
padding: 5px;
}
.node-label:hover {
background-color: #f0f0f0;
}
.toggle-icon {
display: inline-block;
width: 20px;
text-align: center;
}
性能优化
对于大型树结构,可采用虚拟滚动技术。以下为简化版实现思路:
function useVirtualScroll(items, itemHeight, containerRef) {
const [startIndex, setStartIndex] = useState(0)
const visibleCount = Math.ceil(containerRef.current?.clientHeight / itemHeight)
useEffect(() => {
const handleScroll = () => {
const newStart = Math.floor(containerRef.current.scrollTop / itemHeight)
setStartIndex(newStart)
}
containerRef.current.addEventListener('scroll', handleScroll)
return () => containerRef.current.removeEventListener('scroll', handleScroll)
}, [])
return items.slice(startIndex, startIndex + visibleCount)
}
动态加载方案
当数据量极大时,可采用懒加载方式:
async function loadChildren(parentId) {
const response = await fetch(`/api/nodes?parent=${parentId}`)
return response.json()
}
// 在TreeNode组件中
useEffect(() => {
if (isExpanded && !node.children) {
loadChildren(node.id).then(children => {
// 更新数据逻辑
})
}
}, [isExpanded])
交互增强
添加复选框支持:
function TreeNode({ node, onCheck }) {
const handleCheck = (e) => {
e.stopPropagation()
onCheck(node.id, e.target.checked)
}
return (
<div className="tree-node">
<input
type="checkbox"
checked={node.checked}
onChange={handleCheck}
/>
{/* 其余代码 */}
</div>
)
}
注意事项
- 确保每个节点有唯一标识符(如
id) - 深层嵌套时考虑使用不可变数据更新
- 对于超大数据量建议结合后端分页加载
- 可添加动画效果提升用户体验







