vue 递归组件多级_Vue 递归组件构建一个树形菜单

本文详细介绍了如何使用 Vue.js 的递归组件构建一个多级树形菜单。首先,通过 vue-cli 初始化项目,接着定义数据结构,然后创建递归组件并设置条件结束递归,同时在主模块中引入并展示数据。为了优化用户体验,添加了缩进、展开收缩功能以及相应的样式。最后展示了完整代码和注意事项。
摘要由CSDN通过智能技术生成

原标题:Vue 递归组件构建一个树形菜单

Vue.js 中的递归组件是一个可以调用自己的组件例如:

Vue.component('recursive-component', {

template: ``

});

递归组件一般用于博客上显示评论,树形菜单或者嵌套菜单。

一、初始化

我们直接使用 vue 提供的脚手架 vue-cli 来初始化我们的工程:

# 搭建项目

vue init webpack-simple tree-menu

# 进入项目

cd tree-menu

# 依赖安装

npm install

# 运行项目

npm run dev

现在我们的环境已经准备好了,在初始化的项目中,有一些不需要的代码我们可以自己动手删除。

二、数据结构

在本教程中,我们将使用一个树结构的数据来作为我们的每个菜单项,其中每个节点都是一个对象:

label 属性,作为菜单的列表,如果该菜单下还有子菜单,就需要一个 nodes 属性,这是一个或多个节点的数组。

这里需要注意的是像所有的树结构一样,必须有一个根节点,然后从这个根节点进行无限嵌套。

let tree = {

label: 'root',

nodes: [

{

label: 'item1',

nodes: [

{

label: 'item1.1'

},

{

label: 'item1.2',

nodes: [

{

label: 'item1.2.1'

}

]

}

]

},

{

label: 'item2'

}

]

}

三、递归组件

将 src 下的 app.vue 修改为 TreeMenu.vue ,该组件将作为我们的树形的递归组件,用来显示当前节点和自己的子节点。

{{ label }}

v-for="node in nodes"

:nodes="node.nodes"

:label="node.label"

>

<>

export default {

props: [ 'label', 'nodes' ],

name: 'tree-menu'

}

>

如果您正在使用递归组件,则必须使用全局注册 Vue.component,或者给它一个 name 属性。否则,组件的任何子节点将无法解析进一步的调用,并且会得到未定义的组件错误。

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

在我们的树形菜单中,当我们到达一个没有子节点的节点时,我们想要停止递归。你可以用 v-for ,如果 nodes 数组未定义,tree-menu 则不会继续调用组件。

...

四、主模块

在主模块 main.js 中声明一个树形结构的数据,并将该数据作为 data 属性的值,并引入 TreeMenu 组件 。

/* ./src/main.js */

import TreeMenu from './TreeMenu.vue'

let tree = {

...

}

new Vue({

el: '#app',

data: {

tree

},

components: {

TreeMenu

}

})

由于我们的数据结构只有一个单一的根节点。为了开始递归,我们将 TreeMenu 组件作为根节点的子节点,修改根目录下的 index.html 文件:

五、缩进

为了让用户可以直观地识别子组件,就有必要对每一层的子节点进行缩进。为了实现该效果,我们通过添加一个 depth 来实现。使用这个值来动态设置 css 中的 transform 属性,从而实现缩进效果。

{{ label }}

v-for="node in nodes"

:nodes="node.nodes"

:label="node.label"

:depth="depth + 1"

>

<>

export default {

props: [ 'label', 'nodes', 'depth' ],

name: 'tree-menu',

computed: {

indent() {

return { transform: `translate(${this.depth * 50}px)` }

}

}

}

>

同时在 index.html 上将 depth 设置为从零,也就是初始的时候是没有缩进的。这样每次将该值传递给任何子节点时,该值都会增加。

:label="tree.label"

:nodes="tree.nodes"

:depth="0"

>

注意:depth 的值要确保它是一个 Java 数字而不是一个字符串。

六、收缩

在初始的时候,应该只显示根节点,其他节点全部隐藏,然后通过鼠标点击的时候,能够展开各个节点。

为此,我们将添加一个本地状态 showChildren 。如果该值为 false,则不显示子节点,如果为 true 则显示子节点。而这个值应该通过鼠标点击节点来进行切换,所以我们需要一个点击事件:

{{ label }}

v-if="showChildren"

v-for="node in nodes"

:nodes="node.nodes"

:label="node.label"

:depth="depth + 1"

>

<>

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;

}

}

}

>

七、样式

经过以上步骤就已经完成了一个基本的树形菜单。为了使 UI 效果更佳,我们可以添加一个加号和减号图标,使用户界面更加明显。以下是全部的代码:

:label="tree.label"

:nodes="tree.nodes"

:depth="0"

>

/* ./src/main.js */

import Vue from 'vue'

import TreeMenu from './TreeMenu.vue'

import './assets/app.css'

//定义树形菜单数据

let tree = {

label: 'root',

nodes: [

{

label: 'item1',

nodes: [

{

label: 'item1.1'

},

{

label: 'item1.2',

nodes: [

{

label: 'item1.2.1'

}

]

}

]

},

{

label: 'item2'

}

]

}

new Vue({

el: '#app',

// render: h => h(App)

data: {

tree

},

components: {

TreeMenu

}

})

{{ label }}

v-if="showChildren"

v-for="node in nodes"

:nodes="node.nodes"

:label="node.label"

:depth="depth + 1"

>

<>

export default {

props: [ 'label', 'nodes', 'depth' ],

name: 'tree-menu',

data() {

return { showChildren: false }

},

computed: {

iconClasses: function iconClasses() {

return {

'plus': !this.showChildren,

'minus': this.showChildren

};

},

labelClasses: function labelClasses() {

return { 'has-children': this.nodes };

},

indent() {

return { transform: `translate(${this.depth * 50}px)` }

}

},

methods: {

toggleChildren() {

this.showChildren = !this.showChildren;

}

}

}

>

./src/assets/app.css

body {

font-family: sans-serif;

font-size: 18px;

font-weight: 300;

line-height: 1em;

}

.container {

width: 300px;

margin: 0 auto;

}

.tree-menu .label-wrapper {

padding-bottom: 10px;

margin-bottom: 10px;

border-bottom: 1px solid #ccc;

}

.tree-menu .label-wrapper .has-children {

cursor: pointer;

}

.plus:before {

content:'+';

}

.minus:before {

content:'-';

}

本文章由源码时代H5学科讲师原创!

转载须注明出处(http://www.itsource.cn)!感谢大家的配合!返回搜狐,查看更多

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值