效果预览
模拟数据
- 数据来源有很多,可以是自己写死的,也可以是后端调用得到的,也可以从别的组件中拿到
- 这里采用从路由中拿
- 定义数据源src/router/module.js/
const Login = () => import('../views/Login/Login.vue');
const Layout = () => import('../layout/layout.vue');
const Home = () => import('../views/Home.vue');
const User = () => import('../views/About.vue');
const Avatar = () => import('../views/Users/Avatar.vue');
const Password = () => import('../views/Users/Password.vue');
const routes = [
{
path: '/',
redirect: '/home',
},
{
path: '/',
name: 'Layout',
component: Layout,
meta: {
permission: true,
},
children: [
{
path: '/home',
name: 'Home',
component: Home,
meta: {
title: '首页',
icon: '<span class="iconfont icon-shouye"/>', // iconfont图标
inSide: true,
},
},
{
path: '/user',
name: 'User',
component: User,
meta: {
title: '个人中心',
icon: '<span class="iconfont icon-yonghuzhongxin1"/>',
},
children: [
{
path: '/user/avatar',
name: 'Avatar',
component: Avatar,
meta: {
title: '修改头像',
},
children: [
{
path: '/setUp/avatar',
name: 'setUp',
component: Avatar,
meta: {
title: '暂无',
},
},
],
},
{
path: '/user/password',
name: 'Password',
component: Password,
meta: {
title: '修改密码',
},
},
],
},
{
path: '/setUp',
name: 'SetUp',
meta: {
title: '系统设置',
icon: '<span class="iconfont icon-celveguanli"/>',
},
children: [
{
path: '/setUp/avatar',
name: 'setUp',
component: Avatar,
meta: {
title: '暂无',
},
},
{
path: '/setUp/avatar',
name: 'setUp',
component: Avatar,
meta: {
title: '暂无',
},
},
],
},
],
},
{
path: '/login',
name: 'Login',
component: Login,
},
];
export default routes;
递归实现导航栏渲染
- 对于导航栏渲染难点在于不知道有多少层级的导航,可能一级也可能两级或者更多
- 为了方便采用两个组件父组件aside.vue与子组件subAside.vue渲染导航
- 这时候就需要采用递归的方式
- 首先判断哪些数据需要渲染,需要的拿出来
- 判断是否有子节点需要渲染
- 有子节点,递归调用子组件本身
- 没有子节点,返回导航项进行渲染
父组件aside.vue
<template>
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px">
<el-radio-button :label="false">expand</el-radio-button>
<el-radio-button :label="true">collapse</el-radio-button>
</el-radio-group>
<el-menu
default-active="2"
class="el-menu-vertical-demo"
:collapse="isCollapse"
select="handleSelect"
router
unique-opened
>
<!-- 将渲染导航每一项传给子组件渲染,item代表要渲染每一项 -->
<SubAside :isCollapse="isCollapse" v-for="(item,index) in navs" :key="item.path" :menu="item" :index="item.path" />
</el-menu>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import router from '../router/module';
const navs =router.filter((item) => item.meta?.permission)[0].children // 过滤拿到数据
console.log(navs);
const isCollapse = ref(true); // 是否收起,默认不收起
</script>
<style lang="scss" scoped></style>
父组件处理后的用于渲染的数据
[
{
component: () => import('/src/views/Home.vue')
meta: {title: '首页', icon: '<span class="iconfont icon-shouye"/>', inSide: true}
name: "Home"
path: "/home"
},
{
component: () => import('/src/views/About.vue')
meta: {title: '个人中心', icon: '<span class="iconfont icon-yonghuzhongxin1"/>'}
name: "User"
path: "/user"
chilren:[
{
children: [{…}]
component: () => import('/src/views/Users/Avatar.vue?t=1655544364909')
meta: {title: '修改头像'}
name: "Avatar"
path: "/user/avatar"
},
{
component: () => import('/src/views/Users/Password.vue')
meta: {title: '修改密码'}
name: "Password"
path: "/user/password"
}
]
},
{
meta: {title: '系统设置', icon: '<span class="iconfont icon-celveguanli"/>'}
name: "SetUp"
path: "/setUp"
chilren:[
{
component: () => import('/src/views/Users/Avatar.vue?t=1655544364909')
meta: {title: '暂无'}
name: "setUp"
path: "/setUp/avatar"
},
{
component: () => import('/src/views/Users/Avatar.vue?t=1655544364909')
meta: {title: '暂无'}
name: "setUp"
path: "/setUp/avatar"
}
]
}
]
子组件subAside.vue
<template>
<!-- 有子节点渲染这个 -->
<el-sub-menu :index="menu.path" v-if="menu?.children">
<template #title>
<el-icon v-html="menu?.meta.icon"></el-icon>
<span>{{menu?.meta.title}}</span>
</template>
<!-- 递归调用本身,该组件在index.ts中全局注册了 -->
<SubAside v-for="item in menu.children" :menu="item" :isCollapse="isCollapse"/>
</el-sub-menu>
<!-- 没有子节点渲染这个 -->
<el-menu-item v-else :index="menu?.path">
<el-icon v-html="menu?.meta.icon"></el-icon>
<span slot="title">{{menu?.meta.title}}</span>
</el-menu-item >
</template>
<script lang="ts" setup>
import { ref } from "vue"
// 拿到父组件传入的值
defineProps({
isCollapse:Boolean,
menu:Object
})
</script>
<style lang="scss" scoped>
</style>
配置
版本
"vue": "^3.2.25",
"element-plus": "^2.2.6",
main.ts中配置
import { createApp } from 'vue';
import App from './App.vue';
// import 'virtual:windi.css';
import router from './router/index';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import './styles/dark/css-vars.css'
// 引入 Pinia 状态管理工具
import pinia from './stores'
const app=createApp(App)
import SubAside from './components/subAside.vue'
app.component('SubAside', SubAside)
// 注册Element全局可用
app.use(ElementPlus).use(router).use(pinia).mount('#app');
到此这篇关于vue3+Element采用递归调用封装导航栏实现的文章就介绍到这了,更多相关vue3 Element封装导航栏内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!