递归组件的使用

1. 封装简单的Menu组件

一个子组件就相当于一个父组件的html标签
父组件: menu-page.vue
子组件: a-menu.vue、a-menu-item.vue、a-submenu.vue
还有个暴露子组件的index.js

先上一张最后渲染的结果:
图片描述

menu-page.vue

<template>
  <div class="menu-box">
    <a-menu>
      <a-menu-item>111</a-menu-item>
      <a-menu-item>222</a-menu-item>
      <a-submenu>
        <div slot="title">333</div>
        <a-menu-item>333-11</a-menu-item>
        <a-submenu>
          <div slot="title">333-22</div>
          <a-menu-item>333-22-1</a-menu-item>
          <a-menu-item>333-22-2</a-menu-item>
        </a-submenu>
      </a-submenu>
    </a-menu>
  </div>
</template>
<script>
import menuComponents from '_c/menu'
const { AMenu, AMenuItem, ASubmenu } = menuComponents

export default {
  name: 'menu_page',
  components: {
    AMenu,
    AMenuItem,
    ASubmenu
  }
}
</script>

<style lang="less">
.menu-box{
  width: 300px;
  height: 400px;
}
</style>

a-menu.vue

<template>
  <div class="a-menu">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'AMenu'
}
</script>

<style lang="less">
.a-menu{
  & *{
    list-style: none;
  }
  ul{
    padding: 0;
    margin: 0;
  }
}
</style>

a-menu-item.vue

<template>
  <div>
    <li class="a-menu-item"><slot></slot></li>
  </div>
</template>

<script>
export default {
  name: 'AMenuItem'
}
</script>

<style lang="less">
.a-menu-item{
  background: red;
}
</style>

a-submenu.vue

<template>
  <ul class="a-submenu">
    <div class="a-submenu-title" @click="handClick">
      <slot name="title"></slot>
      <span class="shrink-icon" :style="{ transform: `rotateZ(${showChild ? 0 : 180}deg)`}">^</span>
    </div>
    <div v-show="showChild" class="a-submenu-child-box">
      <slot></slot>
    </div>
  </ul>
</template>

<script>
export default {
  name: 'ASubmenu',
  data () {
    return {
      showChild: false
    }
  },
  methods: {
    handClick () {
      this.showChild = !this.showChild
    }
  }
}
</script>

<style lang="less">
.a-submenu{
    background: gray;
    color: #fff;
  &-title{
    position: relative;
    .shrink-icon{
      position: absolute;
      top: 2px;
      right: 10px;
    }
  }
  &-child-box{
    overflow: hidden;
    padding-left: 20px;
  }
  li{
    background: gray;
  }
}
</style>

index.js

import AMenu from './a-menu.vue'
import AMenuItem from './a-menu-item.vue'
import ASubmenu from './a-submenu.vue'

export default{
  AMenu,
  AMenuItem,
  ASubmenu
}

2.递归组件

子组件不断调用自身(需要组件有name属性),接上面组件的例子,修改menu-page.vue,新增re-subname.vue

最后渲染的效果图
图片描述

menu-page.vue

<template>
  <div class="menu-box">
    <a-menu>
      <template v-for="(amenu, index) in list">
        <a-menu-item :key="index" v-if="!amenu.children">{{amenu.title}}</a-menu-item>
        <re-submenu v-else :menu="amenu" :index="index" :key="index"></re-submenu>
      </template>
    </a-menu>
  </div>
</template>
<script>
import menuComponents from '_c/menu'
import reSubmenu from './re-submenu.vue'
const { AMenu, AMenuItem, ASubmenu } = menuComponents

export default {
  name: 'menu_page',
  data () {
    return {
      list: [
        {
          title: '1111'
        },
        {
          title: '2222'
        },
        {
          title: '3333',
          children: [
            {
              title: '3333-1'
            },
            {
              title: '3333-2',
              children: [
                {
                  title: '3333-2-1'
                },
                {
                  title: '3333-2-2'
                },
                {
                  title: '3333-2-3',
                  children: [
                    {
                      title: '3333-2-3-1'
                    },
                    {
                      title: '3333-2-3-2'
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  },
  components: {
    AMenu,
    AMenuItem,
    ASubmenu,
    reSubmenu
  }
}
</script>

<style lang="less">
.menu-box{
  width: 300px;
  height: 400px;
}
</style>

re-subname.vue

<template>
  <a-submenu>
    <div slot="title">{{ menu.title }}</div>
    <template v-for="(amenu, jndex) in menu.children">
      <a-menu-item :key="jndex" v-if="!amenu.children">{{amenu.title}}</a-menu-item>
      <re-submenu v-else :menu="amenu" :index="`${index}_${jndex}`" :key="`${index}_${jndex}`"></re-submenu>
    </template>
  </a-submenu>
</template>

<script>
import menuComponents from '_c/menu'
const { AMenuItem, ASubmenu } = menuComponents

export default {
  name: 'reSubmenu',
  components: {
    AMenuItem,
    ASubmenu
  },
  props: {
    menu: {
      type: Object,
      default: function () {
        return {}
      }
    },
    index: {
      type: Number
    }
  }
}
</script>

岳_轻风
39 声望17 粉丝