Vue3---手写Tree组件
首先我们分析数据的结构
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1'
},
{
lable:'一级-2'
}
]
},
{
lable:'二级',
children:[
{
lable:'二级-1',
children:[
{
lable:'二级-1-1'
},
{
lable:'二级-1-2'
}
]
},
{
lable:'二级-2'
}
]
},
{
lable:'三级'
}
]
对于这种数组类型的取值,首先想到的是
v-for
,但是我们不能准确的知道需要使用多少个v-for。如果所有的子项中children的层级是一样的
(比如下面的情况),那可以使用。但是问题是现在的数据有的item会有4层的children,有的item会有10层的children,层级不一样
,这个时候,我们可以考虑递归
// 数据的格式
// 如果是这种类型的,我们可以使用v-for进行渲染,写两层v-for。label就渲染出来了。
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1'
},
{
lable:'一级-2'
}
]
},
{
lable:'二级',
children:[
{
lable:'二级-1'
},
{
lable:'二级-2'
}
]
},
{
lable:'三级',
children:[
{
lable:'三级-1'
},
{
lable:'三级-2'
}
]
}
]
// 但是这样的数据结构,没法知道具体用几个v-for,这个时候,我们可以考虑用递归的方式解决问题。
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1'
},
{
lable:'一级-2'
}
]
},
{
lable:'二级',
children:[
{
lable:'二级-1',
children:[
{
lable:'二级-1-1'
},
{
lable:'二级-1-2'
}
]
},
{
lable:'二级-2'
}
]
},
{
lable:'三级'
}
]
步骤:
- 定义Tree组件,并挂载到全局上,方便自己调用自己
- 书写Tree组件
// Tree.vue
<template>
<li v-for="(item,index) in Options" :key="index">
{{ item.lable }}
<!-- 当没有children的时候,就不用走递归了。-->
<ul v-if="item.children&&item.children.length">
<MyTree :options="item.children"></MyTree>
</ul>
</li>
</template>
<script setup>
import {ref} from "vue";
const prop = defineProps({
options: {
type: Array,
default: () => {
return []
}
}
})
//console.log(prop.options)
</script>
<style scoped>
</style>
// App.vue
<template>
<h5>树型组件</h5>
<Mtree :options="treeOptions" />
</template>
<script setup>
import {reactive} from "vue";
const state = reactive({
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1'
},
{
lable:'一级-2'
}
]
},
{
lable:'二级',
children:[
{
lable:'二级-1',
children:[
{
lable:'二级-1-1'
},
{
lable:'二级-1-2'
}
]
},
{
lable:'二级-2'
}
]
},
{
lable:'三级'
}
]
})
const { treeOptions } = state
</script>
<style scoped>
</style>
- 添加点击后展开,再点击折叠的功能
=<template>
<li v-for="(item,index) in myOptions" :key="index" @click.stop="openUl(item)">
{{ item.lable }}
<!-- 当没有children的时候,就不用走递归了。-->
<ul v-if="item.children&&item.children.length" v-show="item.isOpen">
<MyTree :options="item.children"></MyTree>
</ul>
</li>
</template>
<script setup>
/*
重点:通过isOpen属性的true,false,来使组件是否v-show隐藏显示
方法:
1 添加myOptions ,使之双向绑定,绑定的数据来自prop.options传来的数据,不直接用prop.options传来数据的原因是因为要加isOpen,防止原数据遭到污染。
const myOptions = ref(prop.options);
2 在递归组件上添加v-show="item.isOpen" 用于显示与隐藏
3 在根组件上添加 @click.stop="openUl(item)"
const openUl= (item)=>{
item.isOpen = !item.isOpen
}
加上.stop为了防止事件冒泡
* */
import {ref} from "vue";
const prop = defineProps({
options: {
type: Array,
default: () => {
return []
}
}
})
//console.log(prop.options)
/*
* 点击以后,展开ul,再点击,关闭ul
* */
const myOptions = ref(prop.options);
const openUl= (item)=>{
item.isOpen = !item.isOpen
}
</script>
<style scoped>
</style>
关于使用v-for 和 递归时候,数据是怎么渲染出来的分析。
// v-for的形式
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1',
//......下面有1万层children
}
]
},
{
lable:'二级',
children:[
{
lable:'二级-1',
//......下面有1万层children
}
]
}
]
// 使用v-for的写法:
<div v-for='(item,index) in Data'>
<div v-for='(childItem,childIndex) in item.children'>
......
</div>
</div>
这个时候会先渲染 "一级" ,再渲染"一级-1","一级-1-1", ...... "一级-1-1...-10000"
紧接着:"二级" ,再渲染"二级-1","二级-1-1", ...... "二级-1-1...-10000"
// 递归的形式
treeOptions:[
{
lable:'一级',
children:[
{
lable:'一级-1',
//......下面有1万层children
}
]
},
{
lable:'二级',
}
]
// 这个时候,如果使用v-for就不行了。为了渲染item1,需要写10000个v-for,但是item2只需要写一个v-for。
// 这个时候,递归发挥作用了,每次都是自己调用自己,传入的值总是children。当children没有的时候,再渲染item2,也就是"二级"
这个时候会先渲染 "一级" ,再渲染"一级-1","一级-1-1", ...... "一级-1-1...-10000"
再渲染 "二级"