在 Vue3 中构建后台系统时,菜单通常是基于一个嵌套结构的数据来动态渲染的。我们可以设计如下的菜单结构:
一、菜单结构可以大致如下
interface MenuItem {
name: string
icon: string
url: string
children?: MenuItem[]
}
export type {MenuItem}
从这个结构可以看出,菜单具有明显的树形结构。处理这种结构时,递归组件是一个非常自然且强大的方案。
二、递归渲染的思路
我们第一步要做的,是判断当前菜单项是否存在子项(即 children)。如果存在,就使用 <el-sub-menu> 渲染;如果不存在,就使用 <el-menu-item>。
Vue 中使用 v-for 进行循环,因此我们可以在模板中通过 v-if 判断是否递归调用自身:
<template>
<el-sub-menu v-if="item.children" :index="item.url">
<template #title>
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{ item.name }}</span>
</template>
<MenuItem v-for="C in item.children" :key="C.url" :item="C"></MenuItem>
</el-sub-menu>
<el-menu-item v-else :index="item.url">
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{ item.name }}</span>
</el-menu-item>
</template>
这段代码实现了组件调用自身,从而完成递归渲染。不过这里有一点非常关键:递归必须有终止条件,否则会导致死循环。终止条件就是当 children 为空或未定义时,不再递归调用自身。
三、总结
整个递归组件的实现思路很清晰:
- 使用
v-if判断是否有子菜单,决定是否递归。 <el-sub-menu>对应有children的节点,<el-menu-item>对应叶子节点。- 合理使用
component :is渲染图标。 - 严格处理终止条件,避免死循环。
- 确保每个菜单项的
url是唯一的,作为key和index。
以上如有错误,欢迎各位进行指点