前言
递归组件,顾名思义就是递归来使用组件(废话)。
递归都会吧,组件也会吧。好了,你已经学会递归组件了(✿◕‿◕✿)
开个玩笑,开始学习递归组件吧
递归组件的使用场景
递归组件的使用场景包括但不限于以下两种情况:
- 用于显示树状结构界面:递归组件能够自我调用,每次调用时传入不同的变量,这使得它非常适合用于显示树状结构界面,比如文件目录、组织结构等。
- 解决迷宫问题:递归是一种有效的解决迷宫问题的算法,它能够通过回溯的方式找出迷宫的所有路径。
总的来说就是当数据层级比较多,且不固定时,那么我们就可以使用递归组件来对需求的实现
递归组件的实现
一般情况下我们所见能遇见的递归组件写法就是结构树
1、首先我们先定义一个List组件
2、然后让它调用他自己
3、这样它就会一直调下去
4、然后就栈溢出了
当然这不是我们想要的,跟递归函数一样,我们需要有一个临界值来控制一下 如下:
vue
List.vue
<template>
<div>
<ul>
<template v-for="(item, index) in lsit" :key="item.id ? item.id : index">
<li>{{ item.name }}</li>
//递归的肯定还是还是一个数组 所以我们要先判断item上是否有这个属性
//如果有且length不等于0的情况下才会继续渲染下去
<List
v-if="item.children?.length"
:lsit="item.children" />
</template>
</ul>
</div>
</template>
//需要接收一下参数
export default {
props:['list']
}
这样我们最基本的递归组件就实现了,来调用一下这个组件,并传入参数试试
vue
Home.vue
<template>
<div>
<List :lsit="list"/>
</div>
</template>
//传入递归组建的参数
const lsits = [
{
name: "22",
id: 1,
children: [
{
name: "33",
id: 11,
children: [
{
name: "44",
id: 111,
children: [
{ name: "55", id: 1111 },
{ name: "56", id: 1112 },
],
},
{ name: "44", id: 111, children: [] },
],
},
{ name: "34", id: 12, children: [{ name: "45", id: 112 }] },
],
},
];
效果: 这么看来,这个递归组件算是基本成功了,但是我们不单单只展现就好了,这么多数据,不会一次性就展示完。现在的需求就是当我们点击父节点,才会显示子节点
这里,我们只需要加上判定来动态修改它的class即可,flag一定要是个数组,否则节点之间展开会相互影响,当flag为空数组时,它所绑定的布尔值全为false
vue
List.vue
<ul ref="idd">
<template v-for="(item, index) in lsit" :key="item.id ? item.id : index">
<li @click.stop="nodeClick(item, index)">{{ item.name }}</li>
<List
v-if="item.children?.length"
:lsit="item.children"
:class="flag[index] ? 'show' : 'hide'" />
</template>
</ul>
export default {
props:['list'],
data() {
return {
flag: [],
};
},
methods: {
nodeClick(v, i) {
flag[i] = !flag[i];
},
},
};
这样就可以点击节点来控制子节点的显示隐藏了
不仅要展示树节点,还要知道我们点击了哪个节点,并将这个节点的数据返回出来,我们可以这样写: 1、定义一个存储节点的变量
2、当点击了这个节点就将点击当前节点的数据赋值给这个变量
3、监听这个变量,如果改变了,那么就会emit出去
4、递归组件,组件内引入了自己,当emit出来之后就会触发node-click事件,触发之后会将值赋值给存储节点的变量,此时那个变量又改变了,会再次emit,直到调出这个递归组件
5、这个时候调用递归组件的父组件就拿到了你点击的那个节点的数据
vue
List.vue
<ul>
<template v-for="(item, index) in lsit" :key="item.id ? item.id : index">
<li @click.stop="nodeClick(item, index)">{{ item.name }}</li>
<List
v-if="item.children?.length"
:lsit="item.children"
:class="flag[index] ? 'show' : 'hide'"
@node-click="node" />
</template>
</ul>
js
export default {
props: ["list"],
data() {
return {
flag: [],
val: "",
};
},
watch: {
//这里深度监听一下,只要改变就emit
val: {
deep: true,
handler(v) {
this.$emit("nodeClick", v);
},
},
},
methods: {
nodeClick(v, i) {
flag[i] = !flag[i];
this.val = v;
},
node(v) {
this.val = v;
},
},
};
这样我们最基本树状结构组件就实现了:

vue3中基本代码实现
vue
List.vue
<template>
<div>
<ul>
<template v-for="(item, index) in lsit" :key="item.id ? item.id : index">
<li @click.stop="nodeClick(item, index)">{{ item.name }}</li>
<List
v-if="item.children?.length"
:lsit="item.children"
:class="flag[index] ? 'show' : 'hide'"
@node-click="node" />
</template>
</ul>
</div>
</template>
<script setup>
import { ref, watchEffect } from "vue";
import List from "./List.vue";
const props = defineProps({
lsit: {
type: Array,
},
});
const emit = defineEmits(["nodeClick"]);
const flag = ref([]);
const shu = ref();
const nodeClick = (v, i) => {
flag.value[i] = !flag.value[i];
shu.value = v;
};
const node = (v) => {
shu.value = v;
};
watchEffect(() => {
if (shu.value) {
emit("nodeClick", shu.value);
}
});
</script>
结尾
递归组件还是很好理解的,原理类似于递归函数,只要处理好了临界值的判定,以及返回值的处理,其他基本上都没有什么。
另外我知道自己的文笔不好,欢迎批评和提出宝贵的建议!