使用vue-router自动生成导航菜单

当我们选用ElementUI作为页面开发的组件库,并打算创建一个如下的菜单表项:

在这里插入图片描述

如果我们采用 ElementUI 库中的 el-menu 组件来实现的话,效果会很不错,但是代码的画风可能是这样的:

    <el-menu>
      <el-submenu index="1">
        <el-menu-item index="1-1"></el-menu-item>
        <el-menu-item index="i-2"></el-menu-item>
      </el-submenu>
      <el-submenu index="2">
        <el-menu-item index="2-1"></el-menu-item>
        <el-menu-item index="2-2"></el-menu-item>
        <el-submenu index="2-3">
          <el-menu-item index="2-3-1"></el-menu-item>
          <el-menu-item index="2-3-2"></el-menu-item>
        </el-submenu>
      </el-submenu>
    </el-menu>

在此简化结构上,每个组件标签还要配上一堆自带属性,代码瞬间就会变得臃肿许多;

再者,随着需求的增多,导航菜单项也不断变长,不仅整个 navigator.vue 文件会变得非常的长,在需求变更时对代码进行维护也比较麻烦。

因此,就有了使用 vue-router 自动配置生成导航菜单的想法,它的原理就是:

拿到 vue-router 的路由配置项 this.$router.options.routes ,为了方便描述,我们简记为 routesroutes 对象如下放示例代码。

routes是一棵多叉树,而恰好我们的菜单就是一颗多叉树,每一个无 children 属性的路由项对应者一个菜单项。

因此,只需要对这个 routes 进行 DFS,我们将当前遍历到的结点记为 cur

cur 是非叶子节点时,我们给它渲染成子菜单组件 < el-submenu >

cur 是叶子节点时,我们就给它渲染成菜单项组件 < el-menu-item >, 并使用 router-link 与 路由中的path响应点击事件进行组件切换

其中,

每个结点的 meta信息中的 title 作为导航项的信息,

每个结点的 path 都需要与之前的进行拼接,并传递给子节点进行拼接,叶子节点将拿到这个拼接好的路径进行导航

const routes = [
    {
        path: '/',
        component: () => import('../views/Home'),
        meta: {title: "组卷管理"},
        children: [
            {
                path: 'hand',
                component: () => import('../views/Home'),
                meta: {title: "手动组卷"}
            },
            {
                path: 'mid',
                component: () => import('../views/Home'),
                meta: {title: "自动组卷"}
            },
        ]
    },
    {
        path: '/exam',
        component: () => import('../views/Home'),
        meta: {title: "考试管理"},
        children: [
            {
                path: 'mid',
                component: () => import('../views/Home'),
                meta: {title: "期中考试"}
            },
            {
                path: 'final',
                component: () => import('../views/Home'),
                meta: {title: "期末考试"}
            },
            {
                path: 'else',
                component: () => import('../views/Home'),
                meta: {title: "其他考试"},
                children: [
                    {
                        path: 'unit',
                        component: () => import('../views/Home'),
                        meta: {title: "单元测验"}
                    },
                    {
                        path: 'daily',
                        component: () => import('../views/Home'),
                        meta: {title: "日常作业"}
                    },
                ]
            },
        ]
    }
]

那我们就开始吧。

(以下只讲具体的思路,与需要注意的点,毕竟全部把代码贴上整篇文章就太长了)

根据上述分析,我们可以创建三种角色的组件 :

(1)根菜单 < Nav-Menu >
(2)子菜单 < Sub-Menu >
(3)菜单项 < Menu-Item >

我们一个一个看:(伪代码)

根菜单:

        <el-menu v-for="cur of routes">
			<sub-menu v-if="cur.childen.length>0"/>
			<menu-item v-else/>
        </el-menu>

从根节点开始对routes深搜,叶子节点渲染成< Menu-Item >,非叶子节点渲染成< Sub-Menu >,并继续深搜,因此需要将当前节点传递给< Sub-Menu >,因此:

< Sub-Menu >需要属性:cur 表示当前节点对象、以及之前拼装好的路由路径

path.resolve(cur.path, pre_url);

因为是递归结构,故< Menu-Item >需要的属性相同。

代码可以改成:(伪代码)

        <sub-menu 
	        v-if="cur.children.length>0" 
	        :cur="cur" 
	        :pre_url="resolvePath(cur.path)"
        />
        <menu-item 
	        v-else 
	        :cur="cur" 
	        :pre_url="resolvePath(cur.path)"
        />

再看比较简单的 < Menu-Item > 菜单项,

菜单项需要的功能就是导航,因此需要一个 < router-link > ,tag的作用是将router-link渲染成div以免显得过于突兀

        <el-menu-item>
            <router-link 
	            tag="div" 
	            :to="resolvePath(cur.path)">
		            {{cur.meta.title}}
            </router-link>
        </el-menu-item>

最后看看 子菜单组件 < Sub-Menu >的设计吧,和同根组件相差无几

<el-submenu :index="idx">
           <template slot="title">
               <i class="el-icon-location"></i>
               <span>{{cur.meta.title}}</span>
           </template>
           <div>
               <template v-for="(item, num) of cur.children">
                   <sub-menu 
	                   v-if="item.children && item.children.length>0" 
	                   :cur="item" 
	                   :pre_url="resolvePath(cur.path)"
	                />
                   <menu-item 
	                   v-else 
	                   :cur="item" 
	                   :pre_url="resolvePath(cur.path)"
                   />
               </template>
           </div>
       </el-submenu>

到这里基本就大功告成了,不过还有几个注意事项:

(1)递归组件时,Vue组件的 name 不能缺省
(2)v-for 每个子结点的 key 用 cur.path 就好了,毕竟路由路径是不会重复的
(3)因为使用的是ElementUI的组件嘛,所以一些必须的属性如 index 还是要配的,这个可以按照配置 pre_url 属性的方法进行拼接。

最后看看实际的成果图:

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值