今天在写一个侧边栏时,用到了递归组件,代码如下
<template>
<div>
<ul class="rightList-container">
<li v-for="(item, i) in list" :key="i" @click="handleClick(item)">
<span :class="{ active: item.isSelected }">{{ item.name }}</span>
<span
v-if="item.aside"
class="aside"
:class="{ active: item.isSelected }"
>{{ item.aside }}</span
>
<!-- 这里的 list 会有子list , 而子list 的结构和样式和当前一样,因此这里就使用 递归组件 -->
<RightList :list="item.childrenList"></RightList>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "RightList", // 给当前组件取个名字,方便递归的时候自己调用自己
props: {
// 右边列表数据的格式 应该是 [{name : xxx , isSelected : xx , childrenList : {name : xxx , isSelected: xx , children : {...}}}]
list: {
type: Array,
default: () => [],
},
},
methods: {
handleClick(item) {
// 右边栏选项点击以后抛出一个事件,只有当当前是未选中状态,点击切换到选中状态才处理,抛出事件由父组件处理
this.$emit("select", item);
},
},
};
但是出现一个问题就是点击子菜单栏(递归的二级菜单栏)会出现冒泡事件, 如下点击一次,会把其一级菜单栏也打印出来
解决方法为 vue 处理事件冒泡的方法, 给click事件添加 .stop 阻止冒泡,如下
<template>
<div>
<ul class="rightList-container">
<li v-for="(item, i) in list" :key="i" @click.stop="handleClick(item)">
<span :class="{ active: item.isSelected }">{{ item.name }}</span>
<span
v-if="item.aside"
class="aside"
:class="{ active: item.isSelected }"
>{{ item.aside }}</span
>
<!-- 这里的 list 会有子list , 而子list 的结构和样式和当前一样,因此这里就使用 递归组件 -->
<RightList :list="item.childrenList"></RightList>
</li>
</ul>
</div>
</template>
解决完冒泡事件后出现了子菜单栏点击发送的 $emit() 失效,具体原因可见这篇博客:https://blog.csdn.net/weixin_44132285/article/details/120323217
解决方法为给递归的子组件添加 v-on=“$listeners”,如下:
<template>
<div>
<ul class="rightList-container">
<li v-for="(item, i) in list" :key="i" @click.stop="handleClick(item)">
<span :class="{ active: item.isSelected }">{{ item.name }}</span>
<span
v-if="item.aside"
class="aside"
:class="{ active: item.isSelected }"
>{{ item.aside }}</span
>
<!-- 这里的 list 会有子list , 而子list 的结构和样式和当前一样,因此这里就使用 递归组件 -->
<RightList :list="item.childrenList" v-on="$listeners"></RightList>
</li>
</ul>
</div>
</template>
就完美解决啦~