递归函数我们都再熟悉不过了,也就是函数自己调用自己。递归组件也是类似的,在组件的template内部使用组件自身。那递归组件有什么使用场景呢? 我们都知道树这个数据结构就是一种递归的结构,因此我们可以用递归组件去实现一个Tree效果,一般可以用于多层级的菜单。
首先是我们的Tree组件,它包含n个TreeNode(树的节点),也就是一级的菜单项。(注: treeData数据摘取自ElementUI的树形组件示例)
<template> <div id="app"> <!-- 一级菜单列表--> <ul> <TreeNode v-for="nodeData in treeData" :nodeData="nodeData" :key="nodeData.label"></TreeNode> </ul> </div> </template> <script> import TreeNode from '../components/TreeNode' export default { name: 'Tree', components: { TreeNode }, data () { return { treeData: [ { label: '一级 1', children: [{ label: '二级 1-1', children: [{ label: '三级 1-1-1' }] }] }, { label: '一级 2', children: [{ label: '二级 2-1', children: [{ label: '三级 2-1-1' }] }, { label: '二级 2-2', children: [{ label: '三级 2-2-1' }] }] }, { label: '一级 3', children: [{ label: '二级 3-1', children: [{ label: '三级 3-1-1' }] }, { label: '二级 3-2', children: [{ label: '三级 3-2-1' }] }] }] } } } </script>
然后我们来实现TreeNode组件。
<template> <li> <!-- 菜单项标题和展开/收起按钮--> <div> <span>{{nodeData.label}}</span> <span v-if="hasChild" @click="open = !open">[{{open ? '-' : '+'}}]</span> </div> <!-- 子菜单 --> <ul v-show="open" v-if="hasChild"> <TreeNode v-for="childNodeData in nodeData.children" :nodeData="childNodeData" :key="childNodeData.label"></TreeNode> </ul> </li> </template> <script> export default { // 必须要写name, 否则在组件内部使用TreeNode标签将无法解析 name: 'TreeNode', props: ['nodeData'], data: function () { return { // 标识展开/收起状态 open: false } }, computed: { // 是否有子菜单 hasChild: function () { return this.nodeData.children && this.nodeData.children.length } } } </script> <style scoped> span { font-size: 36px; } </style>
我们都知道,一级菜单下面可能还包含多个二级菜单,二级菜单下面可能包含多个三级菜单,以此类推...因此,在TreeNode组件的template中我们再次使用了TreeNode组件,从而形成了递归组件。
运行效果如下,点击 + / - 号按钮可以实现展开和收起: