【Vue】Vue框架 之 组件递归

组件递归

当我们编写一个模块的时候,由于不知道数据的嵌套程度有多深,也就不知道组件的元素结构有多少重复的嵌套,而我们此时所用的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匹配了,因此仅仅需要函数名就行,当使用参数的时候会自动带过去的。

猜你喜欢

转载自blog.csdn.net/facial_123/article/details/126824979