vue实现树形菜单组件
vue实现树形菜单组件
数据准备
树形菜单需要递归结构的数据,通常包含id、label、children等字段。例如:
const treeData = [
{
id: 1,
label: '一级菜单',
children: [
{
id: 2,
label: '二级菜单',
children: []
}
]
}
]
递归组件实现
创建TreeItem.vue作为递归组件:

<template>
<li>
<div @click="toggle">
{{ node.label }}
<span v-if="hasChildren">{{ isOpen ? '-' : '+' }}</span>
</div>
<ul v-show="isOpen" v-if="hasChildren">
<tree-item
v-for="child in node.children"
:key="child.id"
:node="child"
/>
</ul>
</li>
</template>
<script>
export default {
name: 'TreeItem',
props: {
node: Object
},
data() {
return {
isOpen: false
}
},
computed: {
hasChildren() {
return this.node.children && this.node.children.length
}
},
methods: {
toggle() {
if (this.hasChildren) {
this.isOpen = !this.isOpen
}
}
}
}
</script>
主组件封装
创建TreeMenu.vue作为主组件:
<template>
<div class="tree-menu">
<ul>
<tree-item
v-for="node in treeData"
:key="node.id"
:node="node"
/>
</ul>
</div>
</template>
<script>
import TreeItem from './TreeItem.vue'
export default {
components: { TreeItem },
props: {
treeData: {
type: Array,
required: true
}
}
}
</script>
<style>
.tree-menu ul {
list-style: none;
padding-left: 20px;
}
.tree-menu li {
cursor: pointer;
user-select: none;
}
</style>
功能扩展
添加复选框支持,修改TreeItem组件:

<template>
<li>
<div @click="toggle">
<input
type="checkbox"
v-model="node.checked"
@click.stop
>
{{ node.label }}
<span v-if="hasChildren">{{ isOpen ? '-' : '+' }}</span>
</div>
<ul v-show="isOpen" v-if="hasChildren">
<tree-item
v-for="child in node.children"
:key="child.id"
:node="child"
@check-change="handleCheckChange"
/>
</ul>
</li>
</template>
<script>
export default {
methods: {
handleCheckChange(checked) {
this.$emit('check-change', checked)
}
}
}
</script>
动态加载
实现懒加载子节点功能:
methods: {
async toggle() {
if (!this.hasChildren && this.node.loadOnDemand) {
try {
const children = await fetchChildren(this.node.id)
this.$set(this.node, 'children', children)
this.isOpen = true
} catch (error) {
console.error(error)
}
} else {
this.isOpen = !this.isOpen
}
}
}
性能优化
对于大型树结构,使用虚拟滚动:
<template>
<virtual-list :size="40" :remain="20">
<tree-item
v-for="node in visibleNodes"
:key="node.id"
:node="node"
/>
</virtual-list>
</template>






