vue实现无限级目录
实现思路
无限级目录通常通过递归组件实现,利用组件自身调用自身的方式处理嵌套数据。Vue的组件系统支持这种递归调用,结合v-for指令可以动态渲染层级结构。
基础数据结构
目录数据通常采用嵌套结构,每个节点包含标题、子节点等信息。示例数据结构如下:
const treeData = [
{
title: '一级目录',
children: [
{
title: '二级目录',
children: [
{ title: '三级目录' }
]
}
]
}
]
递归组件实现
创建名为TreeMenu的递归组件,组件模板中调用自身:
<template>
<ul>
<li v-for="(item, index) in data" :key="index">
{{ item.title }}
<TreeMenu
v-if="item.children && item.children.length"
:data="item.children"
/>
</li>
</ul>
</template>
<script>
export default {
name: 'TreeMenu',
props: {
data: {
type: Array,
required: true
}
}
}
</script>
使用组件
在父组件中引入并传递数据:
<template>
<div>
<TreeMenu :data="treeData" />
</div>
</template>
<script>
import TreeMenu from './TreeMenu.vue'
export default {
components: { TreeMenu },
data() {
return {
treeData: [...] // 你的目录数据
}
}
}
</script>
展开/折叠功能
通过添加isOpen状态控制子目录显示:
<template>
<ul>
<li v-for="(item, index) in data" :key="index">
<span @click="toggle(item)">
{{ item.title }}
</span>
<TreeMenu
v-if="item.children && item.children.length && item.isOpen"
:data="item.children"
/>
</li>
</ul>
</template>
<script>
export default {
methods: {
toggle(item) {
this.$set(item, 'isOpen', !item.isOpen)
}
}
}
</script>
样式优化
添加基础CSS增强视觉效果:
ul {
list-style: none;
padding-left: 20px;
}
li {
cursor: pointer;
margin: 5px 0;
}
性能优化建议
对于大数据量的目录树:
- 使用虚拟滚动技术(如vue-virtual-scroller)
- 添加懒加载功能,初始只渲染可见层级
- 对静态数据使用Object.freeze冻结避免响应式开销
动态加载子节点
实现异步加载子节点功能:
methods: {
async loadChildren(item) {
if (!item.children) {
const res = await api.getChildren(item.id)
this.$set(item, 'children', res.data)
}
this.$set(item, 'isOpen', true)
}
}
完整组件示例
结合上述功能的完整组件示例:
<template>
<ul class="tree-menu">
<li v-for="(item, index) in data" :key="item.id || index">
<div class="node" @click="toggle(item)">
<span>{{ item.title }}</span>
<span v-if="hasChildren(item)">
{{ item.isOpen ? '−' : '+' }}
</span>
</div>
<TreeMenu
v-if="hasChildren(item) && item.isOpen"
:data="item.children"
/>
</li>
</ul>
</template>
<script>
export default {
name: 'TreeMenu',
props: {
data: Array
},
methods: {
hasChildren(item) {
return item.children && item.children.length
},
toggle(item) {
if (this.hasChildren(item)) {
this.$set(item, 'isOpen', !item.isOpen)
}
}
}
}
</script>






