封装简单的树形递归组件

本文介绍了一种基于Vue框架的递归树形组件实现方法,包括如何处理树形数据的展开与折叠,以及如何实现点击高亮效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

最近公司的项目为实现特定的业务需求,要求自行封装一个递归树形组件,毕竟这是第一次自己手写组件,特此记录一下。

公司的项目是用vue框架,我也开了一个vue例子来实现这个功能。模拟数据呢我用了mockjs来模拟后端的请求数据,具体mockjs怎么使用,这里我就不多说了,需要了解的再咨询我吧。


先来看一下效果图(忽略一下样式,哈哈)

image-20201212094501885

这里的树形数据只有三层,当然,项目中是不知道有多少层,这也是为什么要用递归来实现的原因。用递归来实现最重要的一个点在于,递归何时结束,需要有一个条件来限制。既然是树形的结构数据,那就每层的数据都差不多,这就很好解决了

image-20201212095057359

通过上图的数据可以发现,下一层的视图是通过children数据来进行渲染的,那children为空的时候就代表这个分支下面没有子分支了,因此判断条件就可以设置为这个。当然children就要根据你自己拿到的数据改不改名字了

v-if="item[keys.children] && item[keys.children].length"

而递归组件的实现很简单,知道停止的条件就可以写了,具体代码如下

<template>
  <div class="hello">
    <ul>
      <li v-for="(item,index) in treeData" :key="index">
        <div ref="lichange" >
          <span class="rightClass"  @click="handleClick(item)">
            <span>{{ item.label }}</span>
          </span>
          <div class="leftClass">
            <el-input v-model="item.treeNum" @change="putChange(item)"></el-input>
          </div>
        </div>
        <ul v-show="item.isShow" v-if="item.children && item.children.length" >
          <tree ref="tree" :data="item.children" />
        </ul>
      </li>
    </ul>
  </div>
</template>

到了这一步你会发现,树形结构是渲染出来了,但是默认是全部展开的,那要怎么实现像按需点击展开关闭子分支呢?其实也很简单,在上图的treeData数据中,每一个节点都加入一个控制显示隐藏的属性,默认为false,全部隐藏,emmm,具体怎么加上这个属性就仁者见仁智者见智了。然后再设置一个点击事件

handleClick (item) {
   item.isShow = !item.isShow
 }

这样就实现了完整的树形组件需求了。当然这只是这个需求的最基础部分,后面的el-input才是需求的重点,具体就没必要讲了。

一个有点意思的点是,如果你要实现点击实现高亮你会怎么做?

最简单的当然是拿到全部的DOM节点做循环设置背景色样式全部为空,然后设置当前点击的DOM节点高亮,确实是很简单。但是既然是用了vue,当然想想能不能用vue的方法来实现嘛。所以这里我用的是

:class="['liBGchange', active === item.id ? 'active' : '' ]"

liBGchange是高亮的样式名,active是当前点击的节点数据的id属性,如果active与节点数据的id相匹配就加载高亮样式。

问题在于你怎么在递归组件中向上传递当前点击的active?我们都知道,子组件要改变父组件的值需要用到$emit事件,那在递归组件中要怎么一层一层改变父层的active

简单,递归上去呗。

这里就要用到.sync+$emit,不会的可以去百度一下,解答还是很多的。总之.sync是一个语法糖,糅合了v-bind和一个改变v-bind绑定的变量的事件。

实现过程如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DQIFSLbP-1607742137709)(https://i.loli.net/2020/12/12/RoBSa39txUweIbM.png)]

在组件上绑定变量active和一个事件

image-20201212102529801

点击的时候把当前点击节点的id传到父层上,父层会触发update:active事件进而继续向上传递this.$emit('update:active', item.id)事件,直到所有的父层的active都改变了。

全部代码如下

封装的树形组件

<template>
  <div class="hello">
    <ul>
      <li v-for="(item,index) in treeData" :key="index">
        <div ref="lichange" :class="['liBGchange', active === item.id ? 'active' : '' ]" >
          <span class="rightClass"  @click="handleClick(item)">
            <span>{{ item.label }}</span>
          </span>
          <div class="leftClass">
            <el-input v-model="item.treeNum" @change="putChange(item)"></el-input>
          </div>
        </div>
        <ul v-show="item.isShow" v-if="item.children && item.children.length" >
          <tree ref="tree" :data="item.children" :test="test" :active="active" @update:active="$emit('update:active', $event)" />
        </ul>
      </li>
    </ul>
  </div>
</template>

调用tree树形组件

<template>
  <div class="home">
    <tree :data="treeData" v-if="treeData.length>0" :test.sync="treeData[0]" :active.sync="treeData[0].id"/>
  </div>
</template>

特别注意调用组件的时候,这里要写对。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tpA6TzVM-1607742137715)(https://i.loli.net/2020/12/12/HcWZDqNLyrvglu9.png)]

在这里插入图片描述

到这里就实现简单的树形递归组件了,可能我讲的过于啰嗦和混乱,还望见谅啊。

封装一个树形选择组件可以分为以下几个步骤: 1. 定义组件的 props:需要传入一个树形结构的数据,以及选中的节点的值。 ``` props: { treeData: { type: Array, required: true }, selectedValue: { type: String, default: '' } } ``` 2. 在组件中渲染树形结构:可以使用递归组件来渲染,每个节点使用一个复选框来表示是否选中,以及一个展开/收起按钮来展示子节点。 ``` <template> <div> <ul> <li v-for="node in treeData" :key="node.id"> <label> <input type="checkbox" :value="node.value" :checked="isSelected(node)"> {{node.label}} </label> <button v-if="node.children" @click="toggle(node)"> {{node.expanded ? '-' : '+'}} </button> <tree-select v-if="node.children && node.expanded" :treeData="node.children" :selectedValue="selectedValue" @input="onInput"></tree-select> </li> </ul> </div> </template> ``` 3. 实现选中节点的处理:当选中一个节点时,需要将它的值加入到选中节点的列表中;取消选中时,需要将它从选中节点的列表中删除。 ``` methods: { isSelected(node) { return this.selectedValue.includes(node.value) }, onInput(value) { this.$emit('input', value) } } ``` 4. 实现展开/收起节点的处理:当展开/收起一个节点时,需要将它的子节点渲染或移除。 ``` methods: { toggle(node) { node.expanded = !node.expanded } } ``` 最终的代码可以参考下面的例子: ``` <template> <div> <ul> <li v-for="node in treeData" :key="node.id"> <label> <input type="checkbox" :value="node.value" :checked="isSelected(node)"> {{node.label}} </label> <button v-if="node.children" @click="toggle(node)"> {{node.expanded ? '-' : '+'}} </button> <tree-select v-if="node.children && node.expanded" :treeData="node.children" :selectedValue="selectedValue" @input="onInput"></tree-select> </li> </ul> </div> </template> <script> export default { name: 'TreeSelect', props: { treeData: { type: Array, required: true }, selectedValue: { type: String, default: '' } }, methods: { isSelected(node) { return this.selectedValue.includes(node.value) }, onInput(value) { this.$emit('input', value) }, toggle(node) { node.expanded = !node.expanded } } } </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值