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组件递归内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一起来学习Vue的组件化

    目录 背景 定义 分类 优势 首屏加载优化 组件之间的关系 总体上可以分为两大类: 常见使用场景可以分为三类: 总结 说起组件化,我毕设写的就是和组件化相关的. 当时还拿了优,运气! 话不多说,直接进入正文.对于组件化,可以从背景,定义,分类,优势,首屏加载优化,组件之间的关系等方面扯扯,哦不对,是阐述. 背景 假设说采取传统开发网页的方式去开发一个系统,在一定程度上,会造成资源上的严重浪费,编程代码上的冗余等弊端缺陷,会给开发者在增加功能上.版本迭代上.处理业务变更上带来很大的不便.为了避免这

  • Vue 组件化基本使用详情

    目录 1.什么叫做组件化 2.基本使用 前言: 有时候有一组html结构的代码,并且这个上面可能还绑定了事件.然后这段代码可能有多个地方都被使用到了,如果都是拷贝来拷贝去,很多代码都是重复的,包括事件部分的代码都是重复的.那么这时候我们就可以把这些代码封装成一个组件,以后在使用的时候就跟使用普通的html元素一样,拿过来用就可以了. 1.什么叫做组件化 vue.js 有两大法宝,一个是数据驱动,另一个就是组件化,那么问题来了,什么叫做组件化,为什么要组件化?接下来我就针对这两个问题一一解答,所谓

  • Vue生命周期中的组件化你知道吗

    目录 引出生命周期 销毁流程 生命周期 生命周期总结 组件化 非单文件组件 组件的几个注意点 组件的嵌套 VueComponent Vue实例与组件实例 总结 引出生命周期 此时调用change,定时器回调修改opacity,数据修改,模板重新解析,再次调用change. 销毁流程 解绑(自定义)事件监听器 生命周期 生命周期总结 <div id="root"> <!-- <h2 :style="{opacity}">hello,{{n

  • Vue组件化学习之scoped详解

    目录 简介 总结 简介 主要介绍scoped的作用. 先弄一个案例: main.js: //引入vue依赖 import Vue from 'vue' //引入组件App import App from './App.vue' // 关闭生产提示 Vue.config.productionTip = false //创建一个vue实例 new Vue({ //这个目前还没学过,先知道他的作用是将app放入容器中. render: h => h(App), //配置该vue实例管理id为app的容

  • Vue下拉菜单组件化开发详解

    本文实例为大家分享了Vue下拉菜单组件化开发的具体代码,供大家参考,具体内容如下 搞一个自定义组件,只是一个很简单的下拉菜单,也就是一个思路,整起 第一步:在项目中专门创建一个放置自定义组件的文件夹(直接components底下的common中) dropdown.vue 为一级盒子 dropdownMenu.vue 为二级盒子 dropdownItem.vue 为二级盒子内容 第二步 : 对dropdown.vue的操作 <template> <div class="box&

  • Vue组件化常用方法之组件传值与通信

    相关知识点 父组件传值到子组件 子组件传值到父组件 兄弟组件之间传值 祖代和后代之间传值 任意两个组件之间传值 父组件传值到子组件 父组件传值到子组件基本方法有三个,分别为: 属性 props 引用 $refs 子元素 $children 日常开发中,我们用到 props 和 $refs 频率比较多,$children 相对较少些(我就没怎么使用过~). 属性 props 在父组件中添加属性,在子组件中接收使用,例如: 父组件: <HelloWorld msg="Welcome to Yo

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

    目录 前言 效果展示 渲染完整数据 效果如下 获取节点数据 效果如下 动态展开收起 效果如下 完整代码 效果如下 总结 前言 不知道大家有没遇到过这样的场景:渲染列表数据的时候,列表的子项还是列表.如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了. 其实这就是树形结构数据,像常见的组织架构图,文件夹目录,导航菜单等都属于这种结构.很多组件库都带有树形组件,但往往样式不是我们想要的,改起来也非常的费劲.那么,如何自己渲染这些数据呢?答案就是——组件递归! 效果展示

  • vue3组件化开发常用API知识点总结

    目录 组件化思想 组件通讯 $props $emits $parent $attrs proviede & inject 插槽 slot 渲染作用域 作用域插槽 v-model 表单组件 自定义组件 改变默认参数 样式绑定相关 class style 总结 组件化思想 为什么使用组件化开发? 当前前端比较流行的 Vue React 等框架,都会通过编写组件来完成业务需求,也就是组件化开发.包括小程序开发也会用到组件化开发的思想. 分析组件化思想开发应用程序: 将一个完整页面拆分成很多个小组件 每

  • 详解vue.js组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然成为了第一个问题.鉴于目前web前端mvvm框架以及组件化开发方式的流行,决定技术栈采用:vue + es6 + 组件化. 这里首先简单说下web前端组件化开发方式的历程: 最早的组件化结构,代码结构可能如下: - lib/components/calendar |- calendar.css |-

  • Vue组件化开发思考

    一般说到组件,我首先想到的是弹窗,其他就大脑空白了. 因为觉得这个是在项目中最常用的功能,提取出来方便复用的才是组件- 然而我才发现这个想法是有问题的. 我发觉可能从意识上把Vue的组件和UI库的组件(弹窗之类的)混淆了... 缘起于最近的一个表单开发,页面上有2个是联动菜单的选项. 首先想到的是,这个样式和选择地址的那个联动菜单,完全一样哈- (废话,同一个项目 当然要保持ui风格的相同啊!) 不过差别在于 我这个是 一个1级 一个2级, 地址那个是4级的. 然后我就想着把那个地址的组件引进来

  • Vue组件化开发之通用型弹出框的实现

    本文主要分享关于组件化开发的理解,让刚入门的小伙伴少走一些弯路,提高开发效率,作者本人也是新手,如有不当之处,请大佬指出,感谢. 相信很多刚入门的小伙伴,经常会写很多重复的代码,而这些代码一般情况下也都是大同小异,在这种情况下,如何让开发和学习变得更加高效,组件化的思想就显得尤为重要.这里通过设计一个简单的弹出框,给小伙伴们分享组件化的应用. 组件&组件化 组件化是对某些可以进行复用的功能进行封装的标准化工作.组件一般会内含自身的内部UI元素.样式和JS逻辑代码,它可以很方便的在应用的任何地方进

  • Vue关于组件化开发知识点详解

    全局组件注册 Vue.component('first-component', { data: function () { return { count: 0 } }, template: '<button @click="count++">{{ count }}</button>' }) data 必须是一个函数 组件模板内容必须是单个根元素 组件模板内容可以是模板字符串 全局组件可以嵌套全局组件 组件命名方式 Vue.component('first-com

  • vue3组件化开发之可复用性的应用实例详解

    目录 自定义指令 基本结构 自定义 v-loading 指令 插件 基本结构 实现一个全局状态管理插件 Teleport 传送 相关链接 总结 可复用性也是组件化开发的一个优势,能让代码更加简洁优雅.方便维护.下面主要写了vue3中能体现出复用性的一些API的应用. 自定义指令 指令 (Directives) 是带有 v- 前缀的特殊 attribute. v-text v-show v-if ... 等是 vue 中内置的一些指令,当这些内置指令无法满足我们的要求时,这时候也可以使用自定义指令

  • Vue-router 类似Vuex实现组件化开发的示例

    本文介绍了Vue-router 类似Vuex实现组件化开发的示例,分享给大家,具体如下: 随着项目越来越大,把所有route写在一个文件里就显得杂乱. #单个组件路由 import a from '../components/a' export default { path: '/a', name: 'a', component: a } import arouter from 'xxx' export default new Router({ routes: [ arouter ] }) #多

  • Android组件化开发路由的设计实践

    调研了一下目前的路由框架,ARouter(阿里的),ActivityRouter都使用了apt技术 编译时注解,个人想法是一口吃不成胖子,先做个比较实用的. VpRouter路由框架主要应用于组件化开发中 设计目的 解耦 跨模块跳转 方便服务器配置schema,实现动态配置跳转目标 对外部提供远程访问的功能,实现跨应用调用响应 主要功能点 支持intent,http,schema三种跳转 路由表支持xml配置,可自定义,支持多路径 有拦截器 同时支持反射和隐式意图 支持结果回调 支持参数传递 链

随机推荐