用 Vue.js 递归组件实现可折叠的树形菜单(demo)


Posted in Javascript onDecember 25, 2017

用 Vue.js 递归组件实现可折叠的树形菜单(demo) 

在Vue.js中一个递归组件调用的是其本身,如:

Vue.component('recursive-component', {
  template: `<!--Invoking myself!-->
       <recursive-component></recursive-component>`
 });

递归组件常用于在blog上显示注释、嵌套的菜单,或者基本上是父和子相同的类型,尽管具体内容不同。例如:

用 Vue.js 递归组件实现可折叠的树形菜单(demo) 

现在给您演示一下如何有效地使用递归组件,我将通过建立一个可扩展/收缩的树形菜单的来一步步进行。

数据结构

一个树状UI的递归组件将是一些递归数据结构的可视化表达。在本教程中,我们将使用树状结构,其中每个节点都是一个对象:

一个 label 属性。

如果它有子节点,一个 nodes 属性,则它是一个或多个节点的数组属性。

与所有树结构一样,它必须有一个根节点,但可以无限深。

let tree = {
  label: 'root',
  nodes: [
   {
    label: 'item1',
    nodes: [
     {
      label: 'item1.1'
     },
     {
      label: 'item1.2',
      nodes: [
       {
        label: 'item1.2.1'
       }
      ]
     }
    ]
   }, 
   {
    label: 'item2' 
   }
  ]
 }

递归组件

让我们做一个递归组件来显示我们的称为 TreeMenu 的数据结构。它只显示当前节点的标签,并调用自己来显示任何子节点。文件名:TreeMenu.vue,内容如下:

<template>
  <div class="tree-menu">
   <div>{{ label }}</div>
   <tree-menu 
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes' ],
   name: 'tree-menu'
  }
 </script>

如果你使用一个组件递归,必须先给 Vue.component 做一个全局的定义,或者,给它一个 name 属性。否则,任何子组件将无法进一步调用它,你会得到一个不确定的“ undefined component error ”的错误提示。

基本事件

与任何递归函数一样,你需要一个基本事件来结束递归,否则渲染将无限期地继续下去,最终会导致堆栈溢出。

在树菜单中,当我们到达一个没有子节点的节点的时候,我们希望停止递归。你能通过 v-if 做到这一功能,但我们选择使用 v-for 将隐式地为我们实现它;如果 nodes 数组没有任何进一步的定义 tree-menu 组件将被调用。 template.vue 文件如下:

<template>
  <div class="tree-menu">
   ...
   <!--If `nodes` is undefined this will not render-->
   <tree-menu v-for="node in nodes"></tree-menu>
 </template>

使用用法

我们现在如何使用这个组件?首先,我们声明一个 Vue 实例,具有一个数据结构包括 data 属性和定义过的 treemenu 组件。 app.js 文件如下:

import TreeMenu from './TreeMenu.vue'
 let tree = {
  ...
 }
 new Vue({
  el: '#app',
  data: {
   tree
  },
  components: {
   TreeMenu
  }
 })

请记住,我们的数据结构有一个根节点。我们在主模板开始递归调用 TreeMenu 组件,使用根 nodes 属性来props:

<div id="app">
  <tree-menu :label="tree.label" :nodes="tree.nodes"></tree-menu>
 </div>

用 Vue.js 递归组件实现可折叠的树形菜单(demo) 

下面是它目前的样子:

正确的姿势

在视觉上识别子组件的“深度”是很好的,这样用户就可以从UI中获得数据结构的感觉。让我们缩进每一层的子节点来实现这个目标。

用 Vue.js 递归组件实现可折叠的树形菜单(demo) 

这是通过增加一个 depth prop定义,通过 TreeMenu 来实现。我们将使用这个值动态地将内联样式与转换绑定在一起:将使用 transform: translate 的CSS规则为每个节点的标签,从而创建缩进。 template.vue 修改如下**:**

<template>
  <div class="tree-menu">
   <div :style="indent">{{ label }}</div>
   <tree-menu 
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
    :depth="depth + 1"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes', 'depth' ],
   name: 'tree-menu',
   computed: {
    indent() {
     return { transform: `translate(${this.depth * 50}px)` }
    }
   }
  }
 </script>

depth属性在主模板中从零开始。在上面的组件模板中,你可以看到每次传递到任何子节点时这个值都会递增。

<div id="app">
  <tree-menu 
   :label="tree.label" 
   :nodes="tree.nodes"
   :depth="0"
  ></tree-menu>
 </div>

注意:记得 v-bind depth值以确保它是一个JavaScript数字类型而不是字符串。

展开/收起

由于递归数据结构可能很大,所以显示它们的一个很好的UI技巧是隐藏除根节点以外的所有节点,以便用户可以根据需要展开或收起节点。

为此,我们将增加一个局部属性 showChildren 。如果他的值为False,子节点将不会被渲染。此值应通过点击节点切换,所以我们需要使用一个单击事件的监听器方法 toggleChildren 来进行管理。 template.vue 文件修改如下**:**

<template>
  <div class="tree-menu">
   <div :style="indent" @click="toggleChildren">{{ label }}</div>
   <tree-menu 
    v-if="showChildren"
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
    :depth="depth + 1"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes', 'depth' ],
   data() {
    return { showChildren: false }
   },
   name: 'tree-menu',
   computed: {
    indent() {
     return { transform: `translate(${this.depth * 50}px)` }
    }
   },
   methods: {
    toggleChildren() {
     this.showChildren = !this.showChildren;
    }
   }
  }
 </script>

总结

这样,我们就有了一个工作树菜单。用来画龙点睛的一个方法是,你可以添加一个加号/减号图标,这样可以使UI的显示更加明显。我还增加了的很好的字体和计算性能在原来 showChildren 的基础上。

去CodePen( https://codepen.io/anthonygore/pen/PJKNqa)可以看看我是如何实现它的。

用 Vue.js 递归组件实现可折叠的树形菜单(demo) 

Javascript 相关文章推荐
JavaScript 闭包在封装函数时的简单分析
Nov 28 Javascript
JS获取客户端IP地址、MAC和主机名的7个方法汇总
Jul 21 Javascript
jquery图片倾斜层叠切换特效代码分享
Aug 27 Javascript
jQuery+Ajax实现无刷新操作
Jan 04 Javascript
javascript实现获取图片大小及图片等比缩放的方法
Nov 24 Javascript
vue指令以及dom操作详解
Mar 04 Javascript
Node.js 实现远程桌面监控的方法步骤
Jul 02 Javascript
javascript 构建模块化开发过程解析
Sep 11 Javascript
vue循环数组改变点击文字的颜色
Oct 14 Javascript
基于JQuery实现页面定时弹出广告
May 08 jQuery
Nuxt默认模板、默认布局和自定义错误页面的实现
May 11 Javascript
vue-drawer-layout实现手势滑出菜单栏
Nov 19 Vue.js
详解vue-cli之webpack3构建全面提速优化
Dec 25 #Javascript
jQuery EasyUI 选项卡面板tabs的使用实例讲解
Dec 25 #jQuery
zTree树形菜单交互选项卡效果的实现方法
Dec 25 #Javascript
jQuery EasyUI 折叠面板accordion的使用实例(分享)
Dec 25 #jQuery
Vue.js递归组件构建树形菜单
Dec 24 #Javascript
深入浅析JSONAPI在PHP中的应用
Dec 24 #Javascript
Parcel.js + Vue 2.x 极速零配置打包体验教程
Dec 24 #Javascript
You might like
COM in PHP (winows only)
2006/10/09 PHP
PHP实现链式操作的原理详解
2016/09/16 PHP
php7安装mongoDB扩展的方法分析
2017/08/02 PHP
Yii2框架操作数据库的方法分析【以mysql为例】
2019/05/27 PHP
php使用socket调用http和smtp协议实例小结
2019/07/26 PHP
JS中confirm,alert,prompt函数区别分析
2011/01/17 Javascript
一个通过script自定义属性传递配置参数的方法
2014/09/15 Javascript
Javascript验证用户输入URL地址是否为空及格式是否正确
2014/10/09 Javascript
javascript计时器详解
2015/02/28 Javascript
javascript时间戳和日期字符串相互转换代码(超简单)
2016/06/22 Javascript
Jquery组件easyUi实现手风琴(折叠面板)示例
2016/08/23 Javascript
浅谈jquery页面初始化的4种方式
2016/11/27 Javascript
jQuery实现简易的输入框字数计数功能示例
2017/01/16 Javascript
javascript 组合按键事件监听实现代码
2017/02/21 Javascript
JS中getElementsByClassName与classList兼容性问题解决方案分析
2019/08/07 Javascript
webpack的tree shaking的实现方法
2019/09/18 Javascript
Python使用迭代器打印螺旋矩阵的思路及代码示例
2016/07/02 Python
python的构建工具setup.py的方法使用示例
2017/10/23 Python
利用pandas将numpy数组导出生成excel的实例
2018/06/14 Python
python生成以及打开json、csv和txt文件的实例
2018/11/16 Python
在Qt中正确的设置窗体的背景图片的几种方法总结
2019/06/19 Python
python设置环境变量的原因和方法
2019/06/24 Python
利用python numpy+matplotlib绘制股票k线图的方法
2019/06/26 Python
详解Python Celery和RabbitMQ实战教程
2021/01/20 Python
canvas基础之图形验证码的示例
2018/01/02 HTML / CSS
捷克电器和DJ设备网上商店:Electronic-star
2017/07/18 全球购物
Doyoueven官网:澳大利亚健身服饰和配饰品牌
2019/03/24 全球购物
网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?
2016/03/27 面试题
开展批评与自我批评发言材料
2014/05/15 职场文书
医学专业大学生求职信
2014/07/12 职场文书
运动会广播稿200字(10篇)
2014/10/12 职场文书
责任书范本大全
2015/05/11 职场文书
预备党员介绍人意见
2015/06/01 职场文书
西安事变观后感
2015/06/12 职场文书
机械原理课程设计心得体会
2016/01/15 职场文书
世界十大儿童漫画书排名,法国国宝漫画排第五,第二是轰动日本连环
2022/03/18 欧美动漫