Vue 递归组件

1,087 阅读3分钟

递归和递归组件

递归函数简单的定义是:一个函数调用自身。

递归组件:组件调用自身,即:在组件中调用本组件。

递归组件

递归组件产生的条件

  • 结束点:停止递归的情况
  • 一组规则:负责将所有的操作减少到结束点

递归组件的用途

用途:树状视图(用于显示文件夹结构),网站上的注释,嵌套菜单等组件等等

如何编写递归组件

刚开始接触递归组件时,觉得好难,苯宝宝搞不定怎么办。第二次接触,决心化繁为简,一步一步实现一个简单的递归组件。

假设,数据只有一层结构,创建一个data1.js文件,用来保存只有一层的数据

var demoDataSimple = [
  {
    'id': '1',
    'menuName': '基础管理',
    'menuCode': '10',
  },
  {
    'id': '2',
    'menuName': '商品管理',
    'menuCode': ''
  },
  {
    'id': '3',
    'menuName': '订单管理',
    'menuCode': '30'
  },
  {
    'id': '4',
    'menuName': '商家管理',
    'menuCode': ''
  }
]
export default demoDataSimple

如果要渲染这样一个数据,应该怎么办呢?

  1. 首先创建一个demo.vue组件,将data.js引入到组件中
  2. 在组件中使用v-for循环将数据展示出来
<template>
    <div>
      <p>这是一个简单数据结构</p>
      <ul>
        <li :key="item.id" v-for="item in demoArr">
          {{item.menuName}}
        </li>
      </ul>
    </div>
</template>

<script>
  import demoDataSimple from './../../static/data1'
  export default {
    name: 'demo',
    data: function () {
      return {
        demoArr: demoDataSimple
      }
    }
  }
</script>

<style scoped>
</style>

3.将v-for循环的部分抽离出来,放到新的组件Item.vue

deom.vue如下:

<template>
    <div>
      <p>这是一个简单数据结构</p>
      <ul>
        <Item :key="item.id" v-for="item in demoArr" :content="item">
        </Item>
      </ul>
    </div>
</template>

<script>
  import demoDataSimple from './../../static/data1'
  import Item from './Item'
  export default {
    name: 'demo',
    data: function () {
      return {
        demoArr: demoDataSimple
      }
    },
    components: {
      Item
    }
  }
</script>

<style scoped>
</style>

Item.vue如下:

<template>
  <li v-on:click="test">
    {{content.menuName}}
  </li>
</template>
<script>
  export default {
    name: 'Item',
    props: ['content'],
    methods: {
      test: function () {
        console.error(this.content)
      }
    }
  }
</script>

<style scoped>
</style>

恭喜你,到这里万里长征已经走出了第一步。。。。

依据现在的代码来看,循环的部分是Item组件,下面开始正式进入递归组件

1.创建一个data.js文件,用来保存有多层结构的数据,并将其引入到demo.js

var demoData = [
  {
    'id': '1',
    'menuName': '基础管理',
    'menuCode': '10',
    'children': [
      {
        'menuName': '用户管理',
        'menuCode': '11'
      },
      {
        'menuName': '角色管理',
        'menuCode': '12',
        'children': [
          {
            'menuName': '管理员',
            'menuCode': '121'
          },
          {
            'menuName': 'CEO',
            'menuCode': '122'
          },
          {
            'menuName': 'CFO',
            'menuCode': '123'
          },
          {
            'menuName': 'COO',
            'menuCode': '124'
          },
          {
            'menuName': '普通人',
            'menuCode': '124'
          }
        ]
      },
      {
        'menuName': '权限管理',
        'menuCode': '13'
      }
    ]
  },
  {
    'id': '2',
    'menuName': '商品管理',
    'menuCode': ''
  },
  {
    'id': '3',
    'menuName': '订单管理',
    'menuCode': '30',
    'children': [
      {
        'menuName': '订单列表',
        'menuCode': '31'
      },
      {
        'menuName': '退货列表',
        'menuCode': '32',
        'children': []
      }
    ]
  },
  {
    'id': '4',
    'menuName': '商家管理',
    'menuCode': '',
    'children': []
  }
]

export default demoData

2.将上面的Item组件连同他的父组件复制一份,放在下面,将传入的数据变为新的数据

<template>
    <div>
      <p>这是一个简单数据结构</p>
      <ul>
        <Item :key='item.id' v-for='item in demoDataSimple' :content='item'>
        </Item>
      </ul>
      <ul>
        <Item :key='item.id' v-for='item in demoData' :content='item'>
        </Item>
      </ul>
    </div>
</template>

<script>
  import demoDataSimple from './../../static/data1'
  import demoData from './../../static/data'
  import Item from './Item'
  export default {
    name: 'demo',
    data: function () {
      return {
        demoDataSimple: demoDataSimple,
        demoData: demoData
      }
    },
    components: {
      Item
    }
  }
</script>
<style scoped>
</style>

这时第一层数据仍然能正常展示 3.分析数据结构,如果传到Item组件的content有children属性,就代表它有第二层数据结构,那么,接下来处理第二层数据结构,很显然第二层数据是写在Item组件中的,改造后的Item如下:

<template>
  <li v-on:click="test">
    {{content.menuName}}
    <ul v-if="hasChildren">
      <Item :key='item.id' v-for='item in content.children' :content='item'>
      </Item>
    </ul>
  </li>
</template>

<script>
  export default {
    name: 'Item',
    props: ['content'],
    methods: {
      test: function () {
        console.error(this.content)
      }
    },
    computed: {
      hasChildren: function () {
        if (this.content.children && this.content.children.length !== 0) {
          return true
        } else {
          return false
        }
      }
    }
  }
</script>
<style scoped>
</style>

运行,发现不止第二层结构出来了,深层的结构也都出来了,惊喜不已,😁😆😁

注意点

  1. 递归组件必须添加name属性,否则组件自身无法调用自身。
  2. 在父组件中传值是的prop必须和递归组件中传值的prop相同,在上面的例子中为content

参考文章: