概念:
组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事。

之前在写组件时总有些疑惑,为什么export default导出的对象中有个name属性,今天看过递归组件之后,才发现这个name属性的一个比较重要的作用。(当然。name属性的还有其他的用处)。

 

组件递归:
	1、设置组件name名称
	
	2、在模板中直接使用
		<名称 />
		xx-Yy的名称要写成<xxyy />
	
	3、使用时传入参数的方式和组件在其他组件中使用相同,注意递归终止条件
		<xx :props="props">  倘若组件需要传参数

用法:
1、首先我们要知道,既然是递归组件,那么一定要有一个结束的条件,否则就会使用组件循环引用,最终出现“max stack size exceeded”的错误,也就是栈溢出。那么,我们可以使用v-if="false"作为递归组件的结束条件。当遇到v-if为false时,组件将不会再进行渲染。

数据结构:

      list: [
        {
          title: "成人票",
          children: [
            {
              title: "成人三馆联票",
              children: [
                {
                  title: "嘉华酒店自助晚餐",
                  children: [
                    {
                      title: "嘉华酒店自助晚餐-武汉分店",
                    },
                  ],
                },
                {
                  title: "文华酒店自助晚餐",
                },
                {
                  title: "君越酒店自助晚餐",
                },
              ],
            },
            {
              title: "成人五馆联票",
            },
            {
              title: "成人五馆联票 + 酒店",
            },
          ],
        },
        {
          title: "学生票",
          children: [
            {
              title: "古井酒店自助晚餐",
            },
            {
              title: "维斯汀酒店自助晚餐",
            },
            {
              title: "诺富特嘉华酒店自助晚餐",
            },
            {
              title: "全季酒店自助晚餐",
            },
          ],
        },
        {
          title: "儿童票",
        },
        {
          title: "特惠票",
        },
      ],

递归组件:

<template>
  <div class="detail-list">
    <div class="item" v-for="(item, index) in list" :key="index">
      <div class="item-title border-bottom" @click.stop="changeStatus(index)">
        <span class="iconfont icon-round_ticket_fill"></span>
        <span>{{ item.title }}</span>
      </div>
        <!-- 注意递归终止条件 -->
      <div v-if="item.children" class="item-children">
        <!-- 通过name进行递归调用 -->
        <detail-list
          v-if="scopesDefault[index]"
          :list="item.children"
        ></detail-list>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "DetailList",
  props: {
    list: Array,
  },
  
};
</script>
<style scoped>
.item-title {
  line-height: 40px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  color: #666;
}
.item-title .iconfont {
  font-size: 18px;
  margin-right: 5px;
  color: rgb(0, 212, 227);
}
.item-children {
  padding: 0 20px;
}
</style>

总结:
通过props从父组件拿到数据,递归组件每次进行递归的时候都会detail-list组件传递下一级children数据,(大家可以想象一下整个过程),整个过程结束之后,递归也就完成了,当然,这段代码只是简单的做了下递归组件的使用。对于折叠树状菜单来说,我们一般只会去渲染一级的数据,当点击一级菜单时,再去渲染一级菜单下的结构,如此往复。那么v-if就可以实现我们的这个需求,当v-if设置为false时,递归组件将不会再进行渲染,设置为true时,继续渲染 

多级下拉菜单

最后也为大家准备了一个树状折叠菜单的递归组件实现方式~~

<template>
  <div class="detail-list">
    <div class="item" v-for="(item, index) in list" :key="index">
      <div class="item-title border-bottom" @click.stop="changeStatus(index)">
        <span class="iconfont icon-round_ticket_fill"></span>
        <span>{{ item.title }}</span>
      </div>
      <!-- 注意递归终止条件 -->
      <div v-if="item.children" class="item-children">
        <!-- 通过name进行递归调用 -->
        <detail-list
          v-if="scopesDefault[index]"
          :list="item.children"
        ></detail-list>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "DetailList",
  props: {
    list: Array,
  },
  data() {
    return {
      scopesDefault: [], // 第一级
      scopes: [], // 第二级
    };
  },
  created() {
    this.scope();
  },
  methods: {
    changeStatus(index) {
      if (this.scopesDefault[index] == true) {
        this.$set(this.scopesDefault, index, false);
      } else {
        this.$set(this.scopesDefault, index, this.scopes[index]);
      }
    },
    scope() {
      this.list.forEach((item, index) => {
        this.scopesDefault[index] = false; // 第一级索引值全都是false
        if ("children" in item) {// 判断第一级是否有children
          this.scopes[index] = true;
        } else {
          this.scopes[index] = false;
        }
      });
    },
  },
};
</script>
<style scoped>
.item-title {
  line-height: 40px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  color: #666;
}
.item-title .iconfont {
  font-size: 18px;
  margin-right: 5px;
  color: rgb(0, 212, 227);
}
.item-children {
  padding: 0 20px;
}
</style>

 

 

 

 

 

Logo

前往低代码交流专区

更多推荐