Vue组件化开发的必备技能之组件递归

 更新时间:2022年01月29日 10:46:34   作者:前端阿飞  
组件是可以在它们自己的模板中调用自身的,不过它们只能通过 name 选项来做这件事,下面这篇文章主要给大家介绍了关于Vue组件化开发的必备技能之组件递归的相关资料,需要的朋友可以参考下

前言

不知道大家有没遇到过这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了。

其实这就是树形结构数据,像常见的组织架构图,文件夹目录,导航菜单等都属于这种结构。很多组件库都带有树形组件,但往往样式不是我们想要的,改起来也非常的费劲。那么,如何自己渲染这些数据呢?答案就是——组件递归!

效果展示

以上就是使用组件递归,并加入简单交互的展示效果。点击节点会在控制台输出节点对应的数据,如果有子节点,则会展开或收起子节点。接下来我们就看看如何实现以上效果吧!

渲染完整数据

渲染数据这一步非常简单,首先是把树形结构封装成一个列表组件,其次判断每一项有没有子节点,如果有子节点,再使用自身组件去渲染就可以了。

src/components/myTree.vue

<template>
  <div class="tree-item">
    <div v-for="item in treeData" :key="item.id">
      <div class="item-title">{{ item.name }}</div>
      <div v-if="item.children && item.children.length" class="item-childen">
        <my-tree :treeData="item.children"></my-tree>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'myTree',
  props: {
    treeData: {
      type: Array,
      default: () => []
    }
  }
}
</script>

<style lang="scss" scoped>
.tree-item {
  .item-title {
    padding: 4px 8px;
  }
  .item-childen {
    padding-left: 20px;
  }
}
</style>

src/App.vue

<template>
  <my-tree :tree-data="treeData"></my-tree>
</template>

<script>
const treeData = [
  { id: 1, name: '一级1' },
  {
    id: 2,
    name: '一级2',
    children: [
      { id: 3, name: '二级2-1' },
      { id: 4, name: '二级2-2' }
    ]
  },
  {
    id: 5,
    name: '一级3',
    children: [
      {
        id: 6,
        name: '二级3-1',
        children: [
          { id: 7, name: '三级3-1-1' },
          { id: 8, name: '三级3-1-2' }
        ]
      },
      { id: 9, name: '二级3-2' },
      { id: 10, name: '二级3-3' }
    ]
  }
]
import myTree from '@/components/myTree.vue'
export default {
  components: {
    myTree
  },
  data() {
    return {
      treeData: treeData
    }
  }
}
</script>

效果如下

获取节点数据

接下来我们要做的是,点击节点时在控制台输出对应的数据。首先我们使用 $emit,将一级节点的 item 传递出去,也就是子传父的方法,相信大家都会。

其次是将内层节点的数据传递出去,同样使用子传父的方法,只是我们需要给组件里面的 my-tree 绑定@node-click="$emit('node-click', $event)",这样每次子级每次都可以调用父级的 node-click 方法,父级又调用它的父级 node-click 方法,最终调的都是最外层的 node-click 方法,我们只需要在这个过程中,把数据传递过去就可以了。这块有点绕,相信大家多看几遍应该可以看懂。修改如下:

src/components/myTree.vue

<div class="item-title" @click="itemNodeClick(item)">{{ item.name }}</div>
<div v-if="item.children && item.children.length" class="item-childen">
  <my-tree
    :treeData="item.children"
    @node-click="$emit('node-click', $event)"
  ></my-tree>
</div>
...
itemNodeClick(item) {
  this.$emit("node-click", item)
}

src/App.vue

<my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
...
nodeClick(val) {
  console.log(val)
}

效果如下

动态展开收起

这一步的思路是给组件设置一个数组,数组中存放的是当前列表中需要展开的节点的id,当点击节点的时候添加或删除节点id,然后判断每个节点的id在不在这个数组,在则显示子节点,不在则隐藏子节点。

src/components/myTree.vue

<div class="item-title" @click="nodeClick(item)">
  <span>{{ item.name }}</span>
  <span v-if="item.children && item.children.length">
    [{{ isOpen(item.id) ? '-' : '+' }}]
  </span>
</div>
<div
  v-if="item.children && item.children.length"
  v-show="isOpen(item.id)"
  class="item-childen"
>
  <my-tree
    :treeData="item.children"
    @node-click="$emit('node-click', $event)"
  ></my-tree>
</div>
...
data() {
  return {
    expandedKeys: [] // 当前列表需要展开的节点id组成的数组
  }
},
methods: {
  nodeClick(item) {
    this.$emit('node-click', item)
    if (item.children && item.children.length) {
      let index = this.expandedKeys.indexOf(item.id)
      if (index > -1) {
        // 如果当前节点id存在数组中,则删除
        this.expandedKeys.splice(index, 1)
      } else {
        // 如果当前节点id不存在数组中,则添加
        this.expandedKeys.push(item.id)
      }
    }
  },
  isOpen(id) {
    // 判断节点id在不在数组中,在则显示,不在则隐藏
    return this.expandedKeys.includes(id)
  }
}

效果如下

最后我们再添加一些样式,就大功告成啦!

完整代码

src/components/myTree.vue

<template>
  <div class="tree-item">
    <div v-for="item in treeData" :key="item.id">
      <div class="item-title" @click="nodeClick(item)">
        <span>{{ item.name }}</span>
        <span v-if="item.children && item.children.length">
          [{{ isOpen(item.id) ? '-' : '+' }}]
        </span>
      </div>
      <div
        v-if="item.children && item.children.length"
        v-show="isOpen(item.id)"
        class="item-childen"
      >
        <my-tree
          :treeData="item.children"
          @node-click="$emit('node-click', $event)"
        ></my-tree>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'myTree',
  props: {
    treeData: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      expandedKeys: [] // 当前展开的节点id组成的数组
    }
  },
  methods: {
    nodeClick(item) {
      this.$emit('node-click', item)
      if (item.children && item.children.length) {
        let index = this.expandedKeys.indexOf(item.id)
        if (index > -1) {
          // 如果当前节点id存在数组中,则删除
          this.expandedKeys.splice(index, 1)
        } else {
          // 如果当前节点id不存在数组中,则添加
          this.expandedKeys.push(item.id)
        }
      }
    },
    isOpen(id) {
      // 判断节点id在不在数组中,在则显示,不在则隐藏
      return this.expandedKeys.includes(id)
    }
  }
}
</script>

<style lang="scss" scoped>
.tree-item {
  cursor: pointer;
  .item-title {
    padding: 4px 8px;
    &:hover {
      background: #eee;
    }
  }
  .item-childen {
    padding-left: 20px;
  }
}
</style>

src/App.vue

<template>
  <my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
</template>

<script>
const treeData = [
  { id: 1, name: '一级1' },
  {
    id: 2,
    name: '一级2',
    children: [
      { id: 3, name: '二级2-1' },
      { id: 4, name: '二级2-2' }
    ]
  },
  {
    id: 5,
    name: '一级3',
    children: [
      {
        id: 6,
        name: '二级3-1',
        children: [
          { id: 7, name: '三级3-1-1' },
          { id: 8, name: '三级3-1-2' }
        ]
      },
      { id: 9, name: '二级3-2' },
      { id: 10, name: '二级3-3' }
    ]
  }
]
import myTree from '@/components/myTree.vue'
export default {
  components: {
    myTree
  },
  data() {
    return {
      treeData: treeData
    }
  },
  methods: {
    nodeClick(val) {
      console.log(val)
    }
  }
}
</script>

效果如下

以上就是今天的分享!有兴趣的小伙伴可以动手试一哈,把组件进一步封装,或修改成自己想要的样式。 Vue官方的树形视图:cn.vuejs.org/v2/examples…

总结

到此这篇关于Vue组件化开发的必备技能之组件递归的文章就介绍到这了,更多相关Vue组件递归内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于Vue3和Element Plus实现自动导入功能

    基于Vue3和Element Plus实现自动导入功能

    在 Vue 3 项目中,结合  Element Plus  实现自动导入可以显著减少代码量,提升开发效率,Element Plus 提供了官方的自动导入插件  unplugin-vue-components  和  unplugin-auto-import,以下是如何配置和使用的详细步骤,需要的朋友可以参考下
    2025-03-03
  • Vue中通过属性绑定为元素绑定style行内样式的实例代码

    Vue中通过属性绑定为元素绑定style行内样式的实例代码

    这篇文章主要介绍了Vue中通过属性绑定为元素绑定style行内样式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • vue3中使用百度地图的简单步骤

    vue3中使用百度地图的简单步骤

    最近项目要用到百度地图api,好久没用到地图,就百度了一番,下面这篇文章主要给大家介绍了关于vue3中使用百度地图的简单步骤,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • vue定时器设置和关闭页面时关闭定时器方式

    vue定时器设置和关闭页面时关闭定时器方式

    这篇文章主要介绍了vue定时器设置和关闭页面时关闭定时器方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • vue+element项目实时监听div宽度的变化

    vue+element项目实时监听div宽度的变化

    这篇文章主要介绍了vue+element项目里实时监听某个div宽度的变化,然后执行相应的事件,本文结合示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2024-08-08
  • Vue2项目中对百度地图的封装使用详解

    Vue2项目中对百度地图的封装使用详解

    近期项目需求相关地图限定使用百度地图,功能比较简单,下面这篇文章主要给大家介绍了关于Vue2项目中对百度地图的封装使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 前端vue项目如何使用Decimal.js做加减乘除求余运算

    前端vue项目如何使用Decimal.js做加减乘除求余运算

    decimal.js是使用的二进制来计算的,可以更好地实现格化式数学运算,对数字进行高精度处理,使用decimal类型处理数据可以保证数据计算更为精确,这篇文章主要给大家介绍了关于前端vue项目如何使用Decimal.js做加减乘除求余运算的相关资料,需要的朋友可以参考下
    2024-05-05
  • Vue弹窗的两种实现方式实例详解

    Vue弹窗的两种实现方式实例详解

    这篇文章主要介绍了Vue弹窗的两种实现方式,一种使用.sync修饰符另一种使用v-model,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • vue开发拖拽进度条滑动组件

    vue开发拖拽进度条滑动组件

    这篇文章主要为大家详细介绍了vue开发拖拽进度条滑动组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • 如何设置Vue全局公共方法

    如何设置Vue全局公共方法

    这篇文章主要介绍了如何设置Vue全局公共方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05

最新评论