我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
如何将导航菜单与路由结合
路由的数组,他也是层级的关系,而我们的导航菜单与路由数组一样,也是一个层级关系,并且大部分内容都是对应的,一个路由页对应一个菜单按钮。
所以,其实我们可以直接将路由数组,作为我们的导航菜单的数据,我们对他进行遍历。
针对性隐藏
父级
但是不一定每个路由都要显示在菜单栏上,所以我们可以给路由数组的对象,增加一个key值用于判断,这个路由对象是否需要出现在导航栏上,如:
export default [{
path: "/admin",
component: () =>
import ("@/views/admin")
},
{
path: "/form",
component: () =>
import ("@/views/admin"),
hideInMenu: true,
}
]
我们设置了一个hideInMenu属性,他的值为true,那么就表示,这个路由对象,我们不让他显示在菜单栏上,所有在遍历的时候v-if做个判断即可。
当然,我们的路由不可能只有一个层级,他是有可能有子级路由的,所以他会有一个children属性,那么我们可以通过判断是否有children属性,来是否在导航栏菜单增加子级导航菜单
子级
但是,在导航栏上,不一定所有的子级都要遍历上去,所以,我们也可以对是否遍历子级,做一个key值
export default [
{
path: "/admin", component: () => import("@/views/admin")
},
{
path: "/form", component: () => import("@/views/admin"),
hideInMenu: true,
},
{
path: "/child", component: () => import("@/views/admin"),
hideChildrenInMenu: true,
children: [
{ path: "",name:"a", component: () => import("@/views/child/a") },
{ path: "b",name:"b", component: () => import("@/views/child/b") },
]
},
]
hideChildrenInMenu: true
表示这个路由的子级children不显示在导航菜单里。
我们通过这两个key值对这个路由数组进行标识,当导航栏通过这个路由数组进行遍历是,针对性的显示或者隐藏。
获取这个路由数组
既然要通过这个路由数组进行遍历,那么首先我们需要获取到这个数据才行。
有两种方式拿到数据:
1. 通过$router全局的路由根对象获取
this.$router.options.routes
在vue文件里面,通过this来获取到全局$router对象,然后获取他的配置属性options,从配置中我们拿到路由数组routes。
这个routes就是我们配置的路由数组对象了。
import引入
既然我们最终拿到的是routes这个数组,那么我们也可以通过导入的方式获取。
假设我们有一个routes.js文件,他export导出了一个路由数组对象,那么我可以直接引入
import routes from "@/router/routes";
对routes数组进行递归
我们进行一个约定:只有存在name属性的路由,我们才将它显示到菜单上
数据拿到以后,我们肯定是需要做一个遍历操作,将需要的内容,我们提取出来,然后给导航菜单遍历。
那么我们还要考虑到一个问题,就是层级的问题,我们的层级不一定是只有一层,children里面可能还有children。
于是一个不得不进行递归的现实展现在我们面前,我们必须进行递归操作,才能完整拿到需要遍历的数组对象。
于是:
methods: {
getMenuData(routes = []) {
const menuData = [];
routes.forEach((route) => {
//是否显示自己
if (route.name && !route.hideInMenu) {
const newRoute = { ...route }; //解构赋值到新变量上
delete newRoute.children; //删除children,因为需要判断
//是否有子级,并且显示
if (route.children && !route.hideChildrenInMenu) {
//递归并赋值
newRoute.children = this.getMenuData(route.children);
}
//一个判断递归结束
menuData.push(newRoute);
} else if (
!route.hideInMenu &&
!route.hideChildrenInMenu &&
route.children
) {
//有子级,但是不隐藏自己,也不隐藏子级
//拿到子级,并将其提升层级,丢到外面
menuData.push(...this.getMenuData(route.children));
}
});
return menuData;
}
}
我们接受一个数组,并对这个数组进行遍历。
如果这个route对象存在name,并且不隐藏自己,我们就创建一个变量存储这个route对象。
但是我们还要判断这个route对象是否有子级,加上子级有可能是不显示的,所以先进行delete删除,然后if判断
如果这个route存在children,并且不隐藏子级,我们就对他进行递归。
最后递归结束后我们将这个newRoute变量push到总的新数组menuData里面
然后我们还要考虑到一种情况,就是没有name的情况,那么一般来说他可能是这样的路由结构
{
path: "/child", component: () => import("@/views/admin"),
children: [
{ path: "",name:"a", component: () => import("@/views/child/a") },
{ path: "b",name:"b", component: () => import("@/views/child/b") },
]
},
父级里面有一个默认子路由,默认路由有name(父级的name值必须给子级默认路由)
所以
我们还需要判断一下这个情况,一般我们的做法就是将这个路由里面的children提出来,升层,子级变为父级了,当然你也可以将它作为一个子级来做。
我的写法是拿出来升层。
menuData.push(...this.getMenuData(route.children));
先进行递归,拿到数组后,使用扩展运算符将数组里的内容拿出来,push进menuData
如果有必要,也可以写成子级,不升层,那实际上和if(route.nam...)差不多了,那么约定就没啥用了,这个就不多说了。
我们拿到数组之后,就可以进行导航菜单的遍历了。
导航菜单的生成
这里就有一个问题,既然我们不知道层级有多深,那么生成导航菜单时,也是不知道这个菜单要遍历几次的,所以也需要使用递归。
递归有两种方式,一种是组件递归和函数式组件递归。
组件递归
组件有一个name属性,我们设置个name属性,然后直接像组件一样使用
<template>
<div>
<menu/>
</div>
</template>
<script>
export default {
name:"menu",
}
</script>
但是有一点我们需要注意,在使用递归的时候,一定要加一个限制,否则递归无限制,肯定会报错的。
这个限制,在这个导航菜单里,就是层级的数量了,有多少层,我递归的是就递归几次,如果没有了,我们就停止递归。
这个判断无非就是if(children)
判断children是否存在了。
函数式组件递归
函数式组件递归,需要和父组件配合。
父组件
<template>
<div>
<menu/>
</div>
</template>
<script>
import Menu from "./Menu";
export default {
components: {
"menu": Menu,
},
}
</script>
函数式组件
<template functional>
<div>
<menu/>
</div>
</template>
<script>
export default {
props: ["data"],
}
</script>
函数式组件没有name,所以父组件引入这个组件,并注册的时候,那个key值就是name了,只要函数式组件内使用相同key值名称的组件,就可以进行递归。
注意就是在函数式组件内,参数通过props接收,比如上面我们用了一个data。
如果要使用的话和vue组件的用法不一样,如:
<template functional>
<div>
<span>{{props.data.meta.title}}</span>
<menu :key="props.data.path"/>
</div>
</template>
<script>
export default {
props: ["data"],
}
</script>
需要加上props这个前缀。
示例代码
父级组件
<template>
<div class="nav">
<ul>
<template v-for="route in menuData">
<!-- 无子级 -->
<li v-if="!route.children" :key="route.name">{{route.meta.title}}</li>
<!-- 有子级 -->
<menu :data="route" :key="route.name">
</template>
</ul>
</div>
</template>
<script>
import Menu from "./Menu";
export default {
components: {
"menu": Menu,
},
};
</script>
子级函数式组件
<template functional>
<div class="child">
<h2 class="title">{{props.data.meta.title}}</h2>
<ul>
<template v-for="route in props.data.children">
<!-- 无子级 -->
<li v-if="!route.children" :key="route.name">{{route.meta.title}}</li>
<!-- 有子级 -->
<menu :data="route" :key="route.name">
</template>
</ul>
</div>
</template>
<script>
export default {
props: ["data"],
};
</script>
然后基本就是这样了,这个就是我对导航菜单和路由结合的一个理解。
评论(0)