Vue3---手写Tree组件
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"
再渲染 "二级"