Vue3-递归组件实现动态菜单
Vue3-递归组件实现动态菜单
一、前言
递归组件是一种特殊的组件,它在自身模板中调用自己。这种组件结构可以很方便地处理具有嵌套层次关系的数据。
常见应用场景
- 树状结构展示:递归组件可以用来展示树状结构的数据,比如文件夹结构、评论列表等。通过递归地调用自身,可以很容易地处理多层级嵌套的数据,使得代码更简洁清晰。
- 无限滚动列表:当需要实现一个无限滚动列表时,通过递归地渲染每一行数据,可以实现不断加载数据并展示在列表中的效果,提供更好的用户体验。
- 导航菜单:对于导航菜单中的多级菜单,通过递归地渲染每个菜单项以及它们的子菜单,可以实现动态生成多级导航菜单的功能。
说明
本文基于 Vue3+AntdesignUI3 环境搭建一个动态菜单
来介绍递归组件的使用。
二、实践
实践准备
- NavigateItem.vue 自定义菜单项组件
- menu.js 菜单路由文件
- App.vue 根组件
实践过程
- 创建菜单项组件:定义一个用于显示单独菜单项的组件。
- 定义菜单数据结构:创建一个适合存储菜单数据的数据结构,包含唯一标识和子菜单项。
- 创建递归组件:在组件的
template
中使用组件自身,用于渲染多级菜单。 - 处理递归终止条件:定义递归终止的条件,例如当菜单项没有子菜单项时停止递归。
- 渲染菜单:在父组件中使用递归组件并传递菜单数据,来动态生成整个菜单。
NavigateItem.vue
<template>
// 如果没有子菜单则直接渲染
<a-menu-item v-if="!('children' in item)" :key="item.path">
<template #icon><component :is="item.meta.icon" /></template>
<span>{{ item.meta.title }}</span>
</a-menu-item>
// 否则有子菜单
<a-sub-menu v-else :key="'sub' + item.path">
<template #icon><component :is="item.meta.icon" /></template>
<template #title>{{ item.meta.title }}</template>
// 循环渲染子菜单时要判断子菜单是否又包含子菜单,如果包含则调用自身组件递归渲染
<template v-for="child in item.children">
<NavigateItem v-if="'children' in child" :item="child" />
<a-menu-item v-else :key="child.path">{{ child.meta.title }} </a-menu-item>
</template>
</a-sub-menu>
</template>
<script>
// 这里由于要使用到组件自身,故没有使用setup语法糖
export default {
name: "NavigateItem",
props: {
item: {
type: Object,
require: true,
},
}
};
</script>
menu.js
import {
AppstoreOutlined,
AreaChartOutlined,
CreditCardOutlined,
SettingOutlined,
} from "@ant-design/icons-vue";
// 菜单路由
const menuRoute = [
{
path: "/",
name: "root",
redirect: "/dashboard",
component: () => import("@/layout/Index.vue"),
children: [
{
path: "/dashboard",
name: "dashboard",
component: () => import("@/views/home/Dashboard.vue"),
meta: {
title: "工作台",
icon: AppstoreOutlined,
},
},
{
path: "/setting",
name: "setting",
meta: {
title: "设置",
icon: SettingOutlined,
},
children: [
{
path: "/setting/user",
name: "user",
meta: {
title: "个人设置",
},
component: () => import("@/views/setting/UserSetting.vue"),
},
{
path: "/setting/system",
name: "system",
meta: {
title: "系统设置",
},
component: () => import("@/views/setting/SysSetting.vue"),
},
],
},
{
path: "/resume",
name: "resume",
meta: {
title: "简历管理",
icon: CreditCardOutlined,
},
children: [
{
path: "/add",
name: "add",
meta: {
title: "导入简历",
},
component: () => import("@/views/resume/resumeAdd.vue"),
},
],
},
{
path: "/analysis",
name: "analysis",
meta: {
title: "可视化图表分析",
icon: AreaChartOutlined,
},
component: () => import("@/views/analysis/TotalResume.vue"),
},
],
},
];
export default menuRoute;
App.vue
<template>
<a-menu mode="inline" theme="dark" @click="handleMenu">
<NavigateItem v-for="(item,index) in menuRoute[0].children" :item="item" :key="index"/>
</a-menu>
</template>
<script setup>
// 导入自定义菜单组件
import NavigateItem from "@/components/menu/NavigateItem.vue";
// 导入菜单路由数据
import menuRoute from "@/router/routes/menuRoute.js";
// 导入路由对象
import { useRouter } from "vue-router";
const $router = useRouter();
// 实现点击跳转页面
const handleMenu = (item) => {
$router.push(item.key);
};
</script>
结果
实践结果如图所示,菜单被成功地渲染出来了,图标,文字,点击跳转一点毛病没有。
需要注意的是:递归组件需要在组件内部将
name
属性设置为自身方可实现递归调用。
三、总结
本篇文章介绍了什么是递归组件、递归组件如何定义并使用以及递归组件在动态菜单中地使用场景。
注意事项:
- 确保递归终止条件正确设置,以避免无限递归导致页面崩溃。
- 谨慎处理菜单数据的层级关系,确保数据结构符合递归组件的要求。
- 使用Vue的
key
属性标识每个菜单项,以便在数据变化时减少不必要的渲染。 - 注意性能优化,可以利用Vue 3的优化特性如
<template v-for>
和动态组件等进行性能优化。 - 对于大型的动态菜单,考虑使用虚拟滚动(virtual scroll)等技术来提高性能。
总之,递归组件是一种强大的工具,可以简化处理多级嵌套数据的场景,但在使用时需要注意正确设置递归终止条件和处理数据层级关系,同时优化性能和用户体验。
转载自:https://juejin.cn/post/7244809769785475109