递回元件是指同一个元件里不断引用自己,造成重复一层元件包着一层元件的情况,直至该元件所渲染的资料没有满足你设定的 v-if
资料,就代表此递回结束,不会再往下层产生同一个元件。此技巧常用於制作树状结构的 menu。
虽然能应用此技巧的情况不多,但也曾经出现在常见面试题中,因此也值得花时间学习一下。以下会再作详细解说。
以上是多层树状 menu 的示范。
本篇文章会以歌单为例子。
资料结构如下:
menu: [
{
title: "K-POP",
children: [
{
title: 'BTS',
children: [
{
title: "BE",
children: [
{
title: "Life Goes On",
},
{
title: "Dynamite",
},
{
title: "Blue & Gray",
},
],
},
],
}
]
},
{
title: "US-POP",
children: [
{
title: 'Billie Eilish',
children: [ {...} ]
}
]
},
]
需要留意的重点:
title
和 children
属性。title
和 children
属性。children
属性。因此,可以总结出:
children
属性。
children
--> 没开始递回。children
--> 停止递回。就着以上特点,先看看如何规划整段程序码:
黑色就是 <Menu>
元件,即是我们要递回的元件。以下会逐步解说做法。
首先,建立 App.vue。在 App.vue,使用 v-for
把最外层的 title
,即是音乐类别印出来。
App.vue
<div class="menu-bg">
<Menu v-for="item in menu" :key="item.title" :item="item" />
</div>
所有资料
data() {
return {
menu: [
{
title: "K-POP",
children: [{...}]
},
{
title: "US-POP",
children: [{...}]
},
]
}
}
加上一个灰色背景:
.menu-bg {
background: #ededed;
}
Menu.vue:
<template>
<p>
{{ item.title }}
</p>
</template>
export default {
props: {
item: {
type: Object
}
}
};
第一步是把最外层印出来,并在 Menu 元件使用 item
来接收 menu
里的 2 个物件。像是这样:
<div>
<!-- Menu 元件 -->
<p> K-POP </p>
<!-- Menu 元件 -->
<p> US-POP </p>
</div>
结果:
目前还不是树状 menu,只有把最外层印出来。举例说,我们要把 title: K-POP
的 children 里的资料渲染出来。做法很简单:
item.children
取出 children 资料。title
、children
。<Menu />
元件,即是在再次引用自己。Menu.vue
<template>
<div>
<p>
{{ item.title }}
</p>
<Menu v-for="item in item.children" :key="item.title" :item="item" />
</div>
</template>
注意,在元件里引用自己时,需要加入 name
属性:
export default {
// 加入 name,template 才认得
name: 'Menu',
props: {
item: {
type: Object
}
}
};
结果是把所有资料都渲染出来:
打开 Vue 检查工具就一目了然:
但是,现在看来不像树状 menu,因为:
children
属性,会造成渲染错误。v-if
终止递回,以及加入所需的 CSS先加入 CSS,让整体看来像一个树状 menu:
<template>
<div>
<p>
{{ item.title }}
</p>
<Menu
v-for="item in item.children"
:key="item.title"
:item="item"
class="sub-menu"
/>
</div>
</template>
<style scoped>
.sub-menu {
margin-left: 30px;
}
</style>
之後,加入 toggle 开关切换显示子 menu 的内容:
<template>
<div>
<div class="title-bar">
<p>
{{ item.title }}
</p>
<a class="toggle-icon" href="#" @click.prevent="isOpen = !isOpen">
{{ isOpen ? "-" : "+" }}
</a>
</div>
<Menu
v-show="isOpen"
v-for="item in item.children"
:key="item.title"
:item="item"
class="sub-menu"
/>
</div>
</template>
data() {
return {
isOpen: false,
};
}
.title-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 50px;
}
.toggle-icon {
color: grey;
text-decoration: none;
}
结果:
最後,加入 v-if
来设定终止递回的条件:没有 children 属性。
<template>
<div>
<div class="title-bar">
<p>
{{ item.title }}
</p>
<a class="toggle-icon" href="#" @click.prevent="isOpen = !isOpen">
{{ isOpen ? "-" : "+" }}
</a>
</div>
<template v-if="item.children">
<Menu
v-show="isOpen"
v-for="item in item.children"
:key="item.title"
:item="item"
class="sub-menu"
/>
</template>
</div>
</template>
https://codesandbox.io/s/shu-zhuang-menu-db6wu?file=/src/components/Menu.vue
VueJS - Recursive Components
重新认识 Vue.js - 2-2 元件之间的沟通传递
<<: 【DAY 18】数据分析没有这麽难,透过 Microsoft Power BI ,让你事半功倍!
身为一名工程师真的很讨厌被打断工作,当你沈浸在 coding 的世界时,如果有人来吵闹真的会很想揍他...
前言 昨天朋友知道我正在撰写文章,果然是朋友啊XD 毫不留情地被呛爆,写一大堆屁话,却没有介绍嵌入...
在载入 bootstrap 的 js之前 我们可以看到 https://getbootstrap.c...
如何完成Mac的所有信息? 分享阅读本文,恢复最完整的Mac和明确的教学,我们将使用专业的Mac清洁...
工程师太师了: 第15话 杂记: 近期工作了一些障碍, 导致效率低下, 渐渐发现自己对於一件事情必须...