vue递归组件实战之简单树形控件实例代码

所属分类: 网络编程 / JavaScript 阅读数: 1456
收藏 0 赞 0 分享

1、递归组件-简单树形控件预览及问题

 

在编写树形组件时遇到的问题:

  • 组件如何才能递归调用?
  • 递归组件点击事件如何传递?

2、树形控件基本结构及样式

<template>
 <ul class="vue-tree">
  <li class="tree-item">
   <div class="tree-content"><!--节点内容-->
    <div class="expand-arrow"></div><!--展开或收缩节点按钮-->
    <div class="tree-label">小学</div><!--节点文本内容-->
   </div>
   <ul class="sub-tree"><!--子节点-->
    <li class="tree-item expand">
     <div class="tree-content">
      <div class="expand-arrow"></div>
      <div class="tree-label">语文</div>
     </div>
    </li>
    <li class="tree-item">
     <div class="tree-content">
      <div class="expand-arrow"></div>
      <div class="tree-label">数学</div>
     </div>
    </li>
   </ul>
  </li>
 </ul>
</template>

<style lang="stylus">
.vue-tree{
 list-style: none;
 padding: 0;
 margin: 0;
 .tree-item{
  cursor: pointer;
  transition: background-color .2s;
  .tree-content{
   position: relative;
   padding-left: 28px;
   &:hover{
    background-color: #f0f7ff;
   }
  }
  .expand-arrow{
   position: absolute;
   top: 0;
   left: 0;
   width: 28px;
   height: 28px;
   cursor: pointer;
   &::after{
    position: absolute;
    top: 50%;
    left: 50%;
    display: block;
    content: ' ';
    border-width: 5px;
    border-style: solid;
    border-color: transparent;
    border-left-color: #ccc;
    margin: -5px 0 0 -2.5px;
    transition: all .2s;
   }
  }
  &.expand{
   &>.tree-content{
    background-color: #f0f7ff;
    &>.expand-arrow{
     &::after{
      transform: rotate(90deg);
      margin: -2.5px 0 0 -5px;
     }
    }
   }
  }
  .tree-label{
   height: 28px;
   line-height: 28px;
   font-size: 14px;
  }
  .sub-tree{
   display: none;
   list-style: none;
   padding: 0 0 0 28px;
   margin: 0;
  }
  &.expand>.sub-tree{
   display: block;
  }
  &.no-child{
   &>.tree-content{
    &>.expand-arrow{
     display: none;
    }
   }
  }
 }
}
</style>

3、组件目录及数据结构

目录结构

vue-tree

VueTree.vue
TreeItem.vue

树形控件数据结构

let treeData = [
 {
  text: "一级", // 显示的文字
  expand: false, // 默认是否展开
  children: [ // 子节点
   {
    text: "一级-1",
    expand: false,
   },
   {
    text: "一级-2",
    expand: false,
    children: [
     {
      text: "一级-2-1",
      expand: false,
     },
     {
      text: "一级-2-2",
      expand: false,
     }
    ]
   }
  ]
 }
];

3.1、 TreeItem.vue 代码

<template>
 <li class="tree-item" :class="{expand: isExpand, 'no-child': !treeItemData.children || treeItemData.children.length === 0}">
  <div class="tree-content" @click="_clickEvent">
   <div class="expand-arrow" @click.stop="expandTree()"></div>
   <div class="tree-label">{{treeItemData.text}}</div>
  </div>
  <ul class="sub-tree" v-if="treeItemData.children && treeItemData.children.length > 0">
   <!--TreeItem组件中调用TreeItem组件-->
   <TreeItem
    v-for="item in treeItemData.children"
    :tree-item-data="item"
    :key="uuid()"
    :tree-click-event="treeClickEvent"></TreeItem>
  </ul>
 </li>
</template>

<script>
 export default {
  name: "TreeItem",
  props: {
   treeItemData: {
    type: Object,
    default(){
     return {};
    }
   },
   // 节点点击事件
   treeClickEvent: {
    type: Function,
    default() {
     return function () {};
    }
   }
  },
  data(){
   return {
    // 节点是否展开
    isExpand: this.treeItemData.expand || false
   }
  },
  methods: {
   // 展开/收缩
   expandTree(flag){
    if(!this.treeItemData.children || this.treeItemData.children.length === 0){
     return;
    }
    if(typeof flag === 'undefined'){
     flag = !this.isExpand;
    }else{

     flag = !!flag;
    }
    this.isExpand = flag;
   },
   // 创建一个唯一id
   uuid(){
    let str = Math.random().toString(32);
    str = str.substr(2);
    return str;
   },
   // 节点点击事件
   _clickEvent(){
    // 如果有传递事件函数,则调用事件函数并传递当前节点数据及组件
    if(this.treeClickEvent && typeof this.treeClickEvent === 'function'){
     this.treeClickEvent(this.treeItemData, this);
    }
   }
  }
 }
</script>

3.1.1、解决 组件如何才能递归调用? 问题

在组件模板内调用自身 必须明确定义组件的name属性 ,并且递归调用时组件名称就是name属性。如在 TreeItem.vue 组件中组件的name名称为'TreeItem',那么在template中调用时组件名称就必须是 <TreeItem> 。

当然也可以全局注册组件,具体可以查看vue官方文档 递归组件

3.1.2、解决 递归组件点击事件如何传递? 问题

我这里的解决方案是使用 props 将事件函数传递进来,在点击节点的时候调用事件函数,并把相应的数据传递进去。

之前也尝试过使用 $emit 的形式并把数据传递过去,由于是递归组件,这样一直 $emit ,到最外层时传递的数据就变了,比如传递是第3层节点的数据,到最后执行时数据就变成第1层节点的数据了

4、 VueTree.vue 组件

<template>
 <ul class="vue-tree">
  <TreeItem
    v-for="(item, index) in treeData"
    :key="index"
    :treeItemData="item"
    :tree-click-event="treeClickEvent"></TreeItem>
 </ul>
</template>

<script>
 import TreeItem from "./TreeItem";
 export default {
  name: "VueTreeMenu",
  components: {
   TreeItem
  },
  props: {
   // 树形控件数据
   treeData: {
    type: Array,
    default(){
     return [];
    }
   },
   // 节点点击事件
   treeClickEvent: {
    type: Function,
    default() {
     return function () {};
    }
   }
  }
 }
</script>

<style lang="stylus">
.vue-tree{
 list-style: none;
 padding: 0;
 margin: 0;
 .tree-item{
  cursor: pointer;
  transition: background-color .2s;
  .tree-content{
   position: relative;
   padding-left: 28px;
   &:hover{
    background-color: #f0f7ff;
   }
  }
  .expand-arrow{
   position: absolute;
   top: 0;
   left: 0;
   width: 28px;
   height: 28px;
   cursor: pointer;
   &::after{
    position: absolute;
    top: 50%;
    left: 50%;
    display: block;
    content: ' ';
    border-width: 5px;
    border-style: solid;
    border-color: transparent;
    border-left-color: #ccc;
    margin: -5px 0 0 -2.5px;
    transition: all .2s;
   }
  }
  &.expand{
   &>.tree-content{
    background-color: #f0f7ff;
    &>.expand-arrow{
     &::after{
      transform: rotate(90deg);
      margin: -2.5px 0 0 -5px;
     }
    }
   }
  }
  .tree-label{
   height: 28px;
   line-height: 28px;
   font-size: 14px;
  }
  .sub-tree{
   display: none;
   list-style: none;
   padding: 0 0 0 28px;
   margin: 0;
  }
  &.expand>.sub-tree{
   display: block;
  }
  &.no-child{
   &>.tree-content{
    /*padding-left: 0;*/
    &>.expand-arrow{
     display: none;
    }
   }
  }
 }
}
</style>

5、使用树形组件

<template>
 <div class="app" id="app">
  <VueTree :tree-data="treeData2" :tree-click-event="treeClickEvent"></VueTree>
 </div>
</template>

<script>
import VueTree from "./components/vue-tree/VueTree";

export default {
 name: 'app',
 data(){
  return {
   treeData2: [
    {
     text: "一级", // 显示的文字
     expand: false, // 默认是否展开
     children: [
      {
       text: "二级-1",
       expand: false,
      },
      {
       text: "二级-2",
       expand: false,
       children: [
        {
         text: "三级-1",
         expand: false,
        },
        {
         text: "三级-2",
         expand: false,
         children: [
          {
           text: "四级-1",
           expand: false,
          }
         ]
        }
       ]
      }
     ]
    },
    {
     text: "一级-2",
     expand: false
    }
   ]
  }
 },
 methods: {
  treeClickEvent(item, treeItem){
   console.log(item);
  }
 },
 components: {
  VueTree
 }
}
</script>

以上所述是小编给大家介绍的vue递归组件实战之简单树形控件实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

更多精彩内容其他人还在看

学习javascript文件加载优化

这篇文章主要为大家详细介绍了javascript文件加载优化,三种方式实现js文件加载优化,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

jQuery on()绑定动态元素出现的问题小结

jQuery on()方法是官方推荐的绑定事件的一个方法。使用 on() 方法可以给将来动态创建的动态元素绑定指定的事件,通过本文给大家介绍jQuery on()绑定动态元素出现的问题小结,需要的朋友参考下
收藏 0 赞 0 分享

基于JavaScript实现弹出框效果

弹出框在网站页面中是必不可少的一部分,今天借助脚本之家平台给大家分享使用js实现简单的弹出框效果,感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换

这篇文章主要介绍了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

JavaScript深度复制(deep clone)的实现方法

本文给大家介绍JavaScript深度复制(deep clone)的实现方法,涉及到js深度复制相关知识,本文介绍的非常详细,特此分享脚本之家平台供大家参考
收藏 0 赞 0 分享

jQuery实现简单的DIV拖动效果

这篇文章主要介绍了jQuery实现简单的DIV拖动效果,涉及jQuery针对鼠标事件的响应及页面元素的动态操作技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

javascript每日必学之循环

javascript每日必学之循环,本文的主要内容就是循环,死循环时进行bug调式,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

javascript下使用Promise封装FileReader

这篇文章主要介绍了javascript下使用Promise封装FileReader,需要的朋友可以参考下
收藏 0 赞 0 分享

js下将金额数字每三位一逗号分隔

这篇文章主要介绍了js下将金额数字每三位一逗号分隔的相关资料,还附加了一个小功能,小数位保留两位,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

使用jQuery的easydrag插件实现可拖动的DIV弹出框

EasyDrag 是一个用来实现页面元素拖拉的 jQuery 插件。接下来通过本文给大家介绍使用jQuery的easydrag插件实现可拖动的DIV弹出框,感兴趣的朋友一起学习吧
收藏 0 赞 0 分享
查看更多