组件递归
当我们编写一个模块的时候,由于不知道数据的嵌套程度有多深,也就不知道组件的元素结构有多少重复的嵌套,而我们此时所用的Vue
模板是一个静态的模板,于是会出现一种无法完整表达嵌套深度的现象
因此在这种场景下,就会使用到组件递归的概念,就是指在自身组件中使用自己
其关键就是在于注意
- 给组件命名(添加
name
属性) - 传的参数也要能完成递归操作,并且有递归出口
- 在组件中有事件注册的时候要对引用的组件进行事件的注册
下面举一个例子
常常出现嵌套层级的场景,那么便属目录层级居多了,下面就以目录层级的Vue
模板进行举例
假设我们要写一个组件Catalogue.vue
,它的数据是父组件传递给它的一个Array
类型数组,它的data
配置如下
<script>
export default {
name: "Catalogue",
props: {
/* 数据的结构简示
[
{
name: "xxx",
isSelcet: false,
},
{
name: "yyy",
isSelcet: false,
children: [
{
name: "zzz",
isSelect: true
}
]
}
]
*/
list: {
type: Array,
// 如果要返回数组的话, 这里必须是一个函数,直接写数组是不行的
default: () => [],
},
},
};
</script>
那么根据它的数据,我们可以知道,它的模板结构可以写成列表的形式,根组件是一个ul
容器,内部的li
列表就与数据的量以及深度有关系了,因此首先可以根据list
属性进行遍历得到最外层的模板,如下代码所示
<template>
<ul class="catalogue-container">
<li v-for="(item, i) in list" :key="i">
<span :class="{'active': item.isSelect}"> {
{ item.name }} </span>
</li>
</ul>
</template>
但是很明显,这样的书写方式只能展示一层,如果想要根据list
属性中的children
展示未知嵌套深度的组件,就需要根据Vue
配置中的name
属性进行组件递归了,如下代码所示
<template>
<ul class="catalogue-container">
<li v-for="(item, i) in list" :key="i">
<span :class="{'active': item.isSelect}"> {
{ item.name }} </span>
/* 进行组件递归,知道item.children为空 */
<Catalogue :list="item.children">
</li>
</ul>
</template>
到此就相当于模板结构已经完成,下面如果涉及到组件内部事件注册的时候,还需要进行其他的事件注册处理,如下代码
假设组件内部的span
元素需要注册点击事件,而此事件在本组件中无法处理,需要向父元素抛出一个select
事件,也就是说如下的模板与配置:
<template>
<ul class="catalogue-container">
<li v-for="(item, i) in list" :key="i">
<span
:class="{'active': item.isSelect}"
@click="handlerClick(item)"
> {
{ item.name }} </span>
/* 进行组件递归,知道item.children为空 */
<Catalogue :list="item.children">
</li>
</ul>
</template>
<script>
export default {
methods: {
handlerClick(item) {
console.log("Catalogue", item);
this.$emit("select", item);
},
},
};
</script>
这里的问题就很明显,就是当运行起来的时候只有第一层的嵌套span
注册了点击事件,其他深一层的span
都没有注册点击事件,那么就需要在此基础上做一些修改,给递归的组件再次注册一下**select
事件**,代码如下所示
<template>
<ul class="catalogue-container">
<li v-for="(item, i) in list" :key="i">
<span
:class="{'active': item.isSelect}"
@click="handlerClick(item)"
> {
{ item.name }} </span>
<Catalogue
:list="item.children"
@select="handlerClick"
>
</li>
</ul>
</template>
<script>
export default {
methods: {
handlerClick(item) {
console.log("Catalogue", item);
this.$emit("select", item);
},
},
};
</script>
这里进行select
事件注册的时候,只需要给一个handlerClick
函数名即可,因为这个组件相当于是子组件的子组件了,因此也是没有办法处理这个函数的,还需要继续向上抛出select
事件,从而将参数item
和事件名称传递给父组件
注意
对递归后的组件进行事件注册的时候,不能传参数item
因为这样就没有办法和自身的item
匹配了,因此仅仅需要函数名就行,当使用参数的时候会自动带过去的。