经常我们能看到这种结构的元素在Web页面中
这种元素通常被称为Tree组件,像这种每个层级的结构都很类似,但是又要放在一个组件中去实现它,那么就需要用到递归
如何实现一个最简单tree组件
💡基本功能
-
1️⃣ 展开折叠
-
2️⃣ 有层次感
<template>
<div>
<div class="ul" v-for="item in treeData" :key="item.id">
<div
class="li"
:style="{
paddingLeft: level * 27 + 'px'
}"
@click.stop="clickNode(item)"
>
<span
v-if="item.type === 'dir'"
:class="['icon-triangle', item.isOpen ? 'rotate' : 'rotateFalse']"
></span>
<span>{{ item.name }}</span>
</div>
<custom-tree v-if="item.isOpen" :data="item.children" :level="level + 1">
</custom-tree>
</div>
</div>
</template>
<script>
import customTree from "./tree.vue";
export default {
name: "custom-tree",
components: { customTree },
props: {
data: {
type: Array,
default: () => []
},
level: {
type: Number,
default: 1
}
},
data() {
return {
treeData: null
};
},
created() {
this.treeData = this.data;
this.treeData.forEach((node) => {
node.level = this.level;
this.$set(node, "isOpen", true);
});
},
methods: {
clickNode(node) {
node.isOpen = !node.isOpen;
}
}
};
</script>
进阶自定义节点
回忆一下,插槽一般是指父组件定义的内容插入到子组件中,而父组件定义的内容的数据是由父组件提供的
上述组件最终编译结果就是
<div class="father">
<div class="child">
<div>father</div>
</div>
</div>
那么现在我想用到的是子组件的数据呢?
就是用到作用域插槽
<template>
<div class="father">
<child>
<template v-slot="user">
<div>{{user.people.name}}</div>
</template>
</child>
<child>
<template v-slot="{people}">
<div>{{people.name}}</div>
</template>
</child>
</div>
</template>
<script>
import child from './child.vue';
export default{
components: {child},
}
<template>
<div class="child">
<slot :people="child"></slot>
</div>
</template>
<script>
export default{
data(){
return {
child: {
name: '小明',
}
}
}
}
</script>
看过往的例子,容易造成误解的是在子组件中去绑定的属性的名称应该用什么名称
网上的例子基本都是清一色的跟数据的名称一致比如说<slot :child="child"></slot>
实际上可以用任何的名称都可以,因为在父组件中v-slot拿到的数据是一个对象,对象的属性名称就是子组件绑定的属性名称
这个例子来说v-slot="user"
的user实际上就是{"person": {"name":"小明"}}
如果在子组件中用<slot :child="child"></slot>
那么user实际上是{"child":{"name":"小明"}}
基于作用域插槽,将上述的tree进行改造
<template>
<div>
<div class="ul" v-for="item in treeData" :key="item.id">
<slot :node="item" :data="treeData">
<div
class="li"
:style="{
paddingLeft: level * 27 + 'px'
}"
@click.stop="clickNode(item)"
>
<span
v-if="item.type === 'dir'"
:class="['icon-triangle', item.isOpen ? 'rotate' : 'rotateFalse']"
></span>
<span>{{ item.name }}</span>
</div>
</slot>
<custom-tree v-show="item.isOpen" :data="item.children" :level="level + 1">
<template v-slot="{ node, data }">
<slot :node="node" :data="data">
<div
class="li"
:style="{
paddingLeft: level * 27 + 'px'
}"
@click.stop="clickNode(node)"
>
<span
v-if="item.type === 'dir'"
:class="[
'icon-triangle',
item.isOpen ? 'rotate' : 'rotateFalse'
]"
></span>
<span>{{ item.name }}</span>
</div>
</slot>
</template>
</custom-tree>
</div>
</div>
</template
使用的方法,template中拿到子组件传过来的数据,根据这个数据去编写组件内容
<custom-tree :data="data1">
<template v-slot:default="{ node, data }">
<div
class="node"
:style="{
paddingLeft: node.level * 27 + 'px',
backgroundColor: 'rgb(212,235,' + 80 * node.level + ')'
}"
@click="click(node)"
>
<span><input type="radio"></span>
{{ node.name }}
</div>
</template>
</custom-tree>