Vue3:Elemenu-Plus递归型菜单组件封装(2)

        在上一篇文章中,以Vue2的选项式 API风格,封装了递归型菜单组件,但是这其中存在着一些问题,例如:

        【1】子组件menuList.vue中,通过this.$emit()方式定义的事件,在Vue3组合式API风格父组件中可能会被执行多次。之所以说是可能,是因为我这测试中确实遇到了这个问题,子组件中确实只执行了一次,父组件中却执行了两次。

        【2】父子组件之间的props传值风格不同,vue3中,我们通常是通过defineProps API明确定义要传递的props属性,而Vue2中选项式API风格的组件,则直接可以通过export default导出的配置参数进行声明。这种不一致的写法在我写个人项目中,一向是不太赞同的。

         基于以上原因,我将上一篇文章《Vue:Elemenu-Plus递归型菜单组件封装》中的组件进行了改写,改为Vue3组合式API风格的组件,并对上述问题进行了解决。

        示例代码如下,

菜单子组件定义

        示例代码如下,

<!-- 多级菜单组件抽取 -->
<template>
    <el-menu :default-active="props.activeIndex" :class="props.customMenuClass" background-color="transparent"
        text-color="#fff" active-text-color="#ffef40" :mode="props.mode" @select="onSelect" :ellipsis="false">
        <template v-for="(item) in props.items">
            <template v-if="item.children">
                <el-sub-menu :index="item.index" popper-class="el-sub-menu-popper-class">
                    <template #title>
                        <router-link :to="item.path">
                            <el-icon>
                                <component :is="item.meta.icon" :size="24"></component>
                            </el-icon>
                            <span>{
   
   { item.meta.title }}</span>
                        </router-link>
                    </template>
                    <!-- 直接使用文件名即可,不用考虑name的问题 -->
                    <!-- <menu-list :items="item.children" :mode="item.meta.mode" @select="onSelect"></menu-list> -->
                    <index :items="item.children" :mode="item.meta.mode" @select="onSelect"></index>
                </el-sub-menu>
            </template>
            <template v-else>
                <el-menu-item :index="item.index" :key="item.path">
                    <router-link :to="item.path">
                        <el-icon>
                            <component :is="item.meta.icon" :size="24"></component>
                        </el-icon>
                        <span>{
   
   { item.meta.title }}</span>
                    </router-link>
                </el-menu-item>
            </template>
        </template>
    </el-menu>
</template>
<script setup>
import { toRef, watch } from 'vue'
//declare props from parent-component
const props = defineProps({
    customMenuClass: {
        type: String,
        required: false,
        default: "el-menu-class",
    },
    mode: {
        type: String,
        default: "horizontal",
    },
    items: {
        type: Array,
        required: true,
    },
    activeIndex: {
        type: String,
        required: false,
        default: "",
    }
})

//declare emtits by current-component
const emits = defineEmits(["select"])

//declare watch-calls
watch(() => props.activeIndex, (value, oldValue) => {
    console.log('新的activeIndex,', value)
})

//declare methods
const onSelect = (key, keyPath, item) => {
    emits("select", { value: true, key, keyPath, item })
}
</script>
<style lang="scss" scoped>
.el-menu-class {
    .el-menu-item:not(.is-disabled):hover {
        background-color: rgba(127, 255, 212, .3);
    }
}
</style>
<style lang="scss">
.el-sub-menu-popper-class {
    background-color: $base-background-color !important;
}
</style>

父组件调用

            <menu-list :items="menuList" :active-index="activeIndex" mode="horizontal" @select="clickHandler" />


//其中,clickHandler为emit方式定义的子组件事件,函数定义如下
const clickHandler = ({ value, key, keyPath, item }) => {
    console.log('clickHandler', value, key, keyPath)
    console.log("item-value", item)
}

        在此之上,我们还可以完成一些其他操作,例如:菜单路由切换的本地缓存,以保证每次进入项目主页时,路由会自动跳转到上一次操作的页面;结合后端API接口调用,实现真正意义上的动态菜单;通过自定义指令,实现前端页面操作的权限控制等等。

猜你喜欢

转载自blog.csdn.net/weixin_43524214/article/details/131615232