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


Posted in Javascript onAugust 27, 2019

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

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

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

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

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 入门·JavaScript 具有全范围的运算符
Oct 01 Javascript
放弃用你的InnerHTML来输出HTML吧 jQuery Tmpl不详细讲解
Apr 20 Javascript
bootstrapfileinput实现文件自动上传
Nov 08 Javascript
Bootstrap风格的zTree右键菜单
Feb 17 Javascript
vue axios登录请求拦截器
Apr 02 Javascript
通过jquery获取上传文件名称、类型和大小的实现代码
Apr 19 jQuery
微信小程序实现红包雨功能
Jul 11 Javascript
vue动态注册组件实例代码详解
May 30 Javascript
layui(1.0.9)文件上传upload,前后端的实例代码
Sep 26 Javascript
如何在Vue中抽离接口配置文件
Oct 31 Javascript
javascript绘制简单钟表效果
Apr 07 Javascript
在Angular中实现一个级联效果的下拉框的示例代码
May 20 Javascript
vue项目中引入Sass实例方法
Aug 27 #Javascript
package.json配置文件构成详解
Aug 27 #Javascript
axios如何利用promise无痛刷新token的实现方法
Aug 27 #Javascript
解决vue打包后刷新页面报错:Unexpected token
Aug 27 #Javascript
JS用最简单的方法实现四舍五入
Aug 27 #Javascript
微信小程序模板消息推送的两种实现方式
Aug 27 #Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
Aug 27 #Javascript
You might like
PHP下通过file_get_contents的代理使用方法
2011/02/16 PHP
php使用异或实现的加密解密实例
2013/09/04 PHP
新浪SAE云平台下使用codeigniter的数据库配置
2014/06/12 PHP
php链表用法实例分析
2015/07/09 PHP
微信 开发生成带参数的二维码的实例
2016/11/23 PHP
PHP Socket网络操作类定义与用法示例
2017/08/30 PHP
js判断FCKeditor内容是否为空的两种形式
2013/05/14 Javascript
JS localStorage实现本地缓存的方法
2013/06/22 Javascript
Jquery动态更改一张位图的src与Attr的使用
2013/07/31 Javascript
YUI模块开发原理详解
2013/11/18 Javascript
JQuery的Ajax请求实现局部刷新的简单实例
2014/02/11 Javascript
Vue页面骨架屏注入方法
2018/05/13 Javascript
详解vantUI框架在vue项目中的应用踩坑
2018/12/06 Javascript
jQuery实现的3D版图片轮播示例【滑动轮播】
2019/01/18 jQuery
Angular8路由守卫原理和使用方法
2019/08/29 Javascript
vue-model实现简易计算器
2020/08/17 Javascript
[01:14]DOTA2 7.22版本新增神杖效果展示(智力英雄篇)
2019/05/29 DOTA
numpy使用技巧之数组过滤实例代码
2018/02/03 Python
Python网络编程使用select实现socket全双工异步通信功能示例
2018/04/09 Python
Python利用splinter实现浏览器自动化操作方法
2018/05/11 Python
python sys.argv[]用法实例详解
2018/05/25 Python
python使用pandas处理excel文件转为csv文件的方法示例
2019/07/18 Python
如何实现Django Rest framework版本控制
2019/07/25 Python
python实现翻转棋游戏(othello)
2019/07/29 Python
Django ORM 查询管理器源码解析
2019/08/05 Python
Python全栈之列表数据类型详解
2019/10/01 Python
深入浅析CSS3中的Flex布局整理
2020/04/27 HTML / CSS
HTML5 在canvas中绘制文本附效果图
2014/06/23 HTML / CSS
html5使用canvas画空心圆与实心圆
2014/12/15 HTML / CSS
如何为DataGridView添加一个定制的Column Type
2014/01/21 面试题
未受刑事制裁公证证明
2014/09/20 职场文书
单位接收函格式
2015/01/30 职场文书
病人慰问信范文
2015/02/15 职场文书
一文了解MySQL二级索引的查询过程
2022/02/24 MySQL
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
《帝国时代4》赛季预告 新增内容编译器可创造地图
2022/04/03 其他游戏