首页 > 其他分享 >vue组件递归

vue组件递归

时间:2023-11-17 16:01:03浏览次数:37  
标签:vue myTree name 递归 节点 item 组件 id

这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个 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,myTree,name,递归,节点,item,组件,id
From: https://www.cnblogs.com/wp-leonard/p/17838962.html

相关文章

  • vue2.0源码简读(5. 扩展)
    5.1event平时开发工作中,处理组件间的通讯,原生的交互,都离不开事件。对于一个组件元素,不仅仅可以绑定原生的DOM事件,还可以绑定自定义事件,非常灵活和方便。那么接下来从源码角度来看看它的实现原理。为了更加直观,通过一个例子来分析它的实现:letChild={template:'<button......
  • vue2.0源码简读(6. Vue Router)
    6.1路由注册Vue从它的设计上就是一个渐进式JavaScript框架,它本身的核心是解决视图渲染的问题,其它的能力就通过插件的方式来解决。Vue-Router就是官方维护的路由插件,在介绍它的注册实现之前,先来分析一下Vue通用的插件注册原理。Vue.useVue提供了Vue.use的全局API来......
  • vue2.0源码简读(7. Vuex)
    7.1Vuex初始化这一节主要来分析Vuex的初始化过程,它包括安装、Store实例化过程2个方面。安装当在代码中通过importVuexfrom'vuex'的时候,实际上引用的是一个对象,它的定义在src/index.js中:exportdefault{Store,install,version:"__VERSION__",mapSt......
  • vue中created、watch和computed的执行顺序
    总结关于vue中created和watch的执行顺序相对比较简单,而其中computed是通过Object.defineProperty为当前vm进行定义,再到后续创建vNode阶段才去触发执行其get函数,最终执行到计算属性computed对应的逻辑。官网的生命周期图中,initreactivity是晚于beforeCreate......
  • 文字组件里的表格为什么分到两页
    问题:完全可以显示在一页上的表格为什么会分成两页解决方法:选取整个表格》开始》段落》换行和分页》分页组中除孤行控制以外全部取消》确定......
  • GoldenGate高可用管理组件-XAG部署与管理
    适用范围适用于OracleRAC集群环境,并在创建ACFS共享文件系统中部署GoldenGate软件前提下,部署xag高可用组件对GoldenGate进行高可用管理。实施步骤1.安装XAG#在安装OGG的节点安装xag#使用grid用户安装unzipxagpack_7b.zip-d/tmpcd/tmp/xag./xagsetup.sh--install--directo......
  • vue~全局插件和全局方法的注册
    本文介绍如何在vue中定义插件,注册插件和使用插件插件目录/src/plugins插件入口文件/src/plugins/index.jsimportcachefrom'./cache'importmodalfrom'./modal'//安装默认插件,在main.js中引入,通过Vue.use()使用它,因为index.js里使用exportdefault导出,所有在main.js里导入......
  • vue上传视频插件
    视频作为一种信息表达方式,越来越受到人们的关注和喜爱。近年来,随着移动互联网的普及,手机、平板电脑等设备可以随时随地观看视频。在开发网站或移动应用时,上传和展示视频成为一项不可或缺的功能。Vue作为一种现代化JavaScript框架,提供了丰富的开发工具和插件,其中视频上传插件是Vue开......
  • vue实现视频上传功能
    本文实例为大家分享了vue实现视频上传功能的具体代码,供大家参考,具体内容如下环境:vue+TS上传视频+上传到阿里云主要处理前端在vue下上传视频使用的是阿里云的视频点播服务1、需要后台去申请一个开发API,请求阿里云的接口访问控制2、有了开发视频的token,供给前端3、前端去请求阿......
  • vue+pdfh5实现将pdf渲染到页面上
    版本:[email protected]+.netCore6.0webapi方法一:通过访问后端获取二进制数据来渲染前端渲染<template><vol-boxref="box":width="width":height="height"><divid="demo"ref="render"></div></vol......