Vue 递归组件

对于一些有规律的DOM结构,如果我们再一遍遍的编写同样的代码,显然代码是比较繁琐和不科学的,而且自己的工作量会大大增加,

那么有没有一种方法来解决这个问题呢?

答案是肯定的,我们可以通过 递归 方式来生成这个结构,当然在 Vue 模板中也是可以实现的,我们可以在 Vue 的组件中调用自己本身,这样就能达到目的。

当然,在 Vue 中,组件可以递归的调用本身,但是有一些条件:

  • 该组件一定要有 name 属性
  • 要确保递归的调用有终止条件,防止内存溢出

为了方便理解递归组件,我写了一个小小的 Demo:

下面有这样一个数据,我们希望把它做成一个文件树一样的结果:

treeData: {
        title: "Web全栈架构师",
        children: [
          {
            title: "Java架构师"
          },
          {
            title: "JS高级",
            children: [
              {
                title: "ES6"
              },
              {
                title: "动效"
              }
            ]
          },
          {
            title: "Web全栈",
            children: [
              {
                title: "Vue训练营",
                expand: true,
                children: [
                  {
                    title: "组件化"
                  },
                  {
                    title: "源码"
                  },
                  {
                    title: "docker部署"
                  }
                ]
              },
              {
                title: "React",
                children: [
                  {
                    title: "JSX"
                  },
                  {
                    title: "虚拟DOM"
                  }
                ]
              },
              {
                title: "Node"
              }
            ]
          }
        ]
      }
复制代码

先写一个树组件(Item):

<template>
  <li>
    <!-- 点击折叠展开 -->
    <div @click="toggle">
      <!-- 显示内容 -->
      {{model.title}}
      <!-- 显示折叠展开的图标,如果没有下级目录的话,则不显示 -->
      <span v-if="isFolder">[{{open?'-':'+'}}]</span>
    </div>
    <!-- 控制是否显示下级目录 -->
    <ul v-show="open" v-if="isFolder">
      <!-- 重点代码,调用自身,实现递归,绑定数据 -->
      <Item v-for="model in model.children" :model="model" :key="model.title"></Item>
    </ul>
  </li>
</template>
<script>
export default {
  name: "Item",
  // 如果想使用此组件,则需要传递的数据
  props: {
    model: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      // 默认不显示下级目录
      open: false
    };
  },
  computed: {
    // 控制是否有下级目录和显示下级目录
    isFolder() {
      return this.model.children && this.model.children.length;
    }
  },
  methods: {
    // 点击折叠展开的方法
    toggle() {
      if (this.isFolder) {
        this.open = !this.open;
      }
    }
  }
};
</script>
复制代码

具体代码的功能已在上面注释

再次强调,这个组件必须要含有 name 这个属性,如果没有 name 这个属性会造成控件自身不能调用自身,而且调用的时候最好绑定一个 key 值,因为这个 key 值是唯一的标识

另外需要注意,当递归组件的时候,需要有一个条件来终止递归,在这里使用 v-for 隐形条件终止递归

然后在外部组件引入这个组件,并绑定数据使用,如下:

<template>
  <div>
    <ul>
      <!-- 使用这个组件,并绑定数据 -->
      <Item :model="treeData"></Item>
    </ul>
  </div>
</template>
<script>
// 导入递归组件
import Item from "./views/Item";
export default {
  name: "App",
  data() {
    return {
      // 需要实现递归的数据,上面已经给出
      treeData: {...
      }
    };
  },
  components: {
    // 注册组件
    Item
  }
};
</script>
复制代码

上面的代码已经实现了一个递归树形结构了,展示效果如下:

后面是 + 号 的可以点击展开,是 - 号 的可以点击折叠

虽然这篇博客看起来很简单,只是一个小的知识点,但是Vue的递归组件确实是一个非常强大的功能。可以用来实现一些类似文件树、部门树、级联选择这样的组件,在实际业务开发中这些组件也经常用到,还是非常重要的~

### Vue 递归组件的使用方法 在 Vue 中,递归组件是一种能够调用自身的特殊组件。这种设计模式非常适合处理具有层次结构的数据,例如树形结构、嵌套菜单或文件夹列表。 #### 创建递归组件的关键点 1. **命名组件** 在 Vue 的单文件组件中,必须显式地为递归组件指定 `name` 属性。只有这样,组件才能识别自身并实现递归逻辑[^2]。 2. **终止条件** 为了避免无限递归,必须设置清晰的退出条件。通常可以通过判断数据是否存在或者满足特定条件来控制递归停止[^3]。 3. **传递数据** 数据通常是通过 `props` 进行父子组件之间的通信。父组件将数据传递给子组件,而子组件则继续将其部分数据向下传递[^1]。 --- #### 示例:树形结构的递归组件 假设我们要构建一个树形结构的目录展示功能,以下是完整的代码示例: ##### 父组件 (App.vue) ```vue <template> <div id="app"> <!-- 将根节点数据传递给递归组件 --> <TreeItem :tree-data="rootData" /> </div> </template> <script> import TreeItem from './components/TreeItem.vue'; export default { name: 'App', components: { TreeItem, }, data() { return { rootData: [ { title: 'Folder 1', children: [ { title: 'File 1-1' }, { title: 'File 1-2' }, ], }, { title: 'Folder 2', children: [ { title: 'Subfolder 2-1', children: [{ title: 'File 2-1-1' }], }, ], }, ], }; }, }; </script> ``` ##### 子组件 (TreeItem.vue) ```vue <template> <ul> <li v-for="(node, index) in treeData" :key="index"> {{ node.title }} <!-- 判断是否有子节点,如果有,则递归渲染 --> <TreeItem v-if="node.children && node.children.length > 0" :tree-data="node.children" /> </li> </ul> </template> <script> // 定义组件名称以便支持递归 export default { name: 'TreeItem', props: { treeData: { type: Array, required: true, }, }, }; </script> ``` --- #### 关键解析 1. **递归的核心机制** - 在 `TreeItem.vue` 中,通过 `<TreeItem>` 调用了自身。 - 使用 `v-if` 控制递归是否继续执行,从而避免无限循环[^3]。 2. **数据流的设计** - 父组件 (`App.vue`) 提供顶层数据并通过 `props` 传递给子组件。 - 子组件接收数据后,进一步拆解并将剩余的部分再次传递给下一层递归实例。 3. **动态生成 DOM 结构** - 使用 `v-for` 遍历数组中的每一项,并根据其内容决定是否需要继续递归渲染子节点[^4]。 --- #### 实际应用场景 递归组件广泛应用于以下场景: - 文件管理器中的文件夹和子文件夹展示[^1]。 - 多级导航菜单的实现[^4]。 - 注释系统的回复链展示[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值