Tree 组件是递归类组件的典型代表,它常用于文件夹、组织架构、生物分类、国家地区等等,世间万物的大多数结构都是树形结构。使用树控件可以完整展现其中的层级关系,并具有展开收起选择等交互功能。
如图所示,我们要实现的就是这样一个效果。之前我们写树状结构都是用jQuery来实现的,用Vue怎么实现呢?
一、数据部分模拟
menuList:[
{
title:'菜单1',
children:[
{
title:'菜单1-1',
children:[
{title:'菜单1-1-1'},
{title:'菜单1-1-2'},
{title:'菜单1-1-3'}
]
},
{title:'菜单1-2'},
{title:'菜单1-3'}
]
},
{title:'菜单2'},
{title:'菜单3'}
]
二、组件各部分实现
Menu.vue
首先我们来写个menu组件,这里放个ul列表,里面的内容,用插槽来表示。
<template>
<ul>
<slot></slot>
</ul>
</template>
<script>
export default {
name: "Menu"
}
</script>
MenuItem.vue
如果没有子节点,所要展示的标题
<template>
<li><slot></slot></li>
</template>
<script>
export default {
name: "MenuItem"
}
</script>
SubMenu.vue
<template>
<div>
<div class="title" @click="change">
<slot name="title"></slot>
<!--name='title'区分插槽 -->
</div>
<div class="sub" v-show="flag">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "SubMenu",
data(){
return {flag:false}
},
methods:{
change(){
this.flag=!this.flag
}
}
}
</script>
<style>
.sub{
padding-left:20px;
}
</style>
三、递归组件ReSubMenu.vue
这里到了我们实现树形结构思想的重点,即递归组件。当我们重复判断有没有子节点,并做出相应的展示的时候,这里就可以使用递归组件了。方便快捷,你值得拥有。
由于有子节点会循环SubMenu这部分操作,所以单独提出来放到ReSubMenu组件中
<template>
<SubMenu>
<template #title><!--#title为了标识区分插槽-->
{{data.title}}
</template>
<template v-for="child in data.children">
<MenuItem :key="child.title" v-if="!child.children">{{child.title}}</MenuItem>
<!--ReSubMenu跟name的名字保持一致,相当于循环使用该组件-->
<ReSubMenu v-else :key="child.title" :data="child"></ReSubMenu>
</template>
</SubMenu>
</template>
<script>
import SubMenu from './SubMenu'
import MenuItem from './MenuItem'
export default {
name: "ReSubMenu",//可以使用递归组件
props:{
data:{
type:Object,//属性校验,为对象数据类型,并且如果没有赋值,默认给一个空对象
default:()=>({})
}
},
components:{
SubMenu,
MenuItem
}
}
</script>
四、整合实现
<div id="app">
<Menu>
<template v-for="menu in menuList">
<MenuItem :key="menu.title" v-if="!menu.children">{{menu.title}}</MenuItem>
<!--这部分是如果有孩子节点则会循环这部分操作,所以要单独提取出来-->
<!-- <SubMenu v-else>
<template #title>{{menu.title}}</template>
<template v-for="child in menu.children">
<MenuItem>{{child.title}}</MenuItem>
</template>
</SubMenu>-->
<ReSubMenu :key="menu.title" v-else :data="menu"></ReSubMenu>
</template>
</Menu>
<div>
<script>
import Menu from './Menu'
import MenuItem from './MenuItem'
// import SubMenu from './SubMenu'
import ReSubMenu from './ReSubMenu'
export default {
data(){
return{
//这里的数据我就模拟一个了
menuList:[
{
title:'菜单1',
children:[
{
title:'菜单1-1',
children:[
{title:'菜单1-1-1'},
{title:'菜单1-1-2'},
{title:'菜单1-1-3'}
]
},
{title:'菜单1-2'},
{title:'菜单1-3'}
]
},
{title:'菜单2'},
{title:'菜单3'}
]
}
},
components:{
Menu,MenuItem,ReSubMenu
}
}
</script>
注:本节部分语句参考 https://juejin.cn/book/6844733759942557704/section/6844733760152272910
。