vue3构建view admin后台管理系统(3)——基于Vue Router实现导航栏

系列文章目录

vue3构建view admin后台管理系统(1)——技术选型
vue3构建view admin后台管理系统(2)——Vue Router使用详解
vue3构建view admin后台管理系统(3)——基于Vue Router实现导航栏



前言

上篇文章我们讲解了Vue Router路由管理工具基本的使用,但是如果同学们没有项目经验,即时看明白官方文档,也很难真正将所学应用到项目中,造成一看就懂,一用就废的尴尬局面。

如何把Vue Router真正应用到项目中,才是我们的重中之重。

其实写这篇文章之前,我是很犹豫的,因为很多东西,原理很简单,应用就略麻烦,写出来就更麻烦,最主要的是,我也不知道写完了,会不会有帮助。在这里抛砖引玉,有不当的地方,欢迎大佬指正。


在这里插入图片描述

一、嵌套路由

本篇文章有一个躲不过的基础概念——Vue Router的嵌套路由。

详细概念请一定移步官网仔细阅读,这里只是做个概述,帮助曾经阅读过官网的同学唤起记忆。

我们在路由定义文件router.js中定义过路由数据:

const routes = [{
    
     path: '/user/:id', component: User }]

我们在页面入口文件APP.vue中定义过路由入口:

<router-view></router-view>

当我们在main.js中引入router插件,就会自动把routes中定义的路由信息拿到,然后根据我们定义的事件,把对应的组件渲染到router-view中。

这里的意思就是把User组件渲染到router-view的位置。

如果User组件中还存在router-view标签,就是路由嵌套,则会在User中的router-view位置渲染routes中定义的对应的children内容。如:

const routes = [
  {
    
    
    path: '/user/:id',
    component: User,
    children: [
      {
    
    
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
    
    
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]

如果User组件的内容如下:

const User = {
    
    
  template: `
    <div class="user">
      <h2>User {
    
    {
    
     $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `,
}

那么就会在User组件中的router-view渲染UserProfile和UserPosts。

以上就是路由嵌套的大体内容。User可以视为父组件,UserProfile和UserPosts则为子组件。

二、使用步骤

1.梳理文件关系

如果对router插件的使用有疑惑的同学请翻阅我上篇文章,这里假定你已经对router的基本使用都熟悉了。

我们先梳理清楚路由涉及的几个主要组件:

  1. APP.vue (基本每个vue项目都有)
  2. layout.vue (我自定义的布局组件,有些框架里也叫main.vue、home.vue之类的)
  3. SideMenu.vue (我自定义的导航组件,包含在layout.vue中)
  4. 各个业务的组件

上节有提及App.vue中存在路由的入口

<router-view></router-view>

router.js中的数据都会渲染在这里。router.js代码精简后,只留下几条数据:

import Login from 'module-base/views/Login/Login.vue'
import Layout from 'module-base/views/Layout/Layout.vue'
export const routes = [
    {
    
    
        path: '/login',
        name: 'login',
        component: Login, //默认直接加载
        meta: {
    
    
            hideInMenu: true
        }
    },
    {
    
    
        path: '/home',
        name: 'home',
        component: Layout,
        children: [
            {
    
    
                path: '/home_page',
                name: 'home_page',
                meta: {
    
    
                    title: '首页',
                    icon: 'ios-list-box'

                },
                component: () => import('module-base/views/Home.vue')
            }
        ]
    },
    {
    
    
        path: '/mailSend',
        name: 'mailSend',
        component: Layout,
        meta: {
    
    
            showAlways: true,
            title: '表单',
            icon: 'ios-list-box',
            // hideInMenu: true

        },
        children: [
            {
    
    
                path: '/mailSend_page',
                name: 'mailSend_page',
                meta: {
    
    
                    title: '表单示例',
                    icon: 'ios-list-box'

                },
                component: () => import('@/views/MailSend/MailSend.vue')
            }
        ]
    }
]

router.js里面有两个组件Login、Layout是先加载,然后定义到component属性,而其他组件都是通过以下代码加载:

() => import('@/views/MailSend/MailSend.vue')

这是一种动态加载组件的方式,可以做到按需加载,优化速度。

我们可以发现,除了“path: ‘/login’”部分,是直接加载login,后面的都定义了一个属性:

component: Layout

这说明我们每个业务页面,其实都是嵌套在Layout组件中,Layout就是我们所有页面的父组件,其他业务页面则是子组件,如children中定义的Home.vue、MailSend.vue。

2.了解布局组件Layout .vue

我们把Layout.vue中的主要html代码摘抄出来:

 <div class="layout">
    <Layout style="height: 100%">
      <Sider ref="side1" style="min-width: 100px" hide-trigger collapsible :collapsed-width="78" v-model="isCollapsed">
        <SideMenu ref="sideMenu" :menu-list="menuList" @on-select="turnToPage_sideMenu"></SideMenu>
        <svg class="icon icon-menu" @click="collapsedSider" aria-hidden="true">
          <use xlink:href="#icon-pendant-full"></use>
        </svg>
      </Sider>

      <Layout>
        <Content :style="{overflowY:'auto',overflowX:'auto', background: '#fff', minHeight: '260px'}">
          <!--router-view嵌套,这里渲染Layout路由的children路由          -->
          <router-view v-slot="{Component}">
            <KeepAlive :max="10">
              <component :is="Component"></component>
            </KeepAlive>
          </router-view>
        </Content>
      </Layout>
    </Layout>
  </div>

这段代码十分简单,主要分为两部分,一个是Sider,就是左侧的导航栏,一个是Layout(iview框架提供的布局标签),就是右侧渲染组件内容的区域,Layout标签里有router-view标签,这里和App.vue中的router-view就组成了路由嵌套。所以子组件Home.vue、MailSend.vue等都会渲染在这里。

Layout标签部分没什么好说的,都是router插件的基础应用。

Sider标签中的svg是应用的阿里iconfont,看不明白可忽略。这部分的核心其实是SideMenu组件

3.了解导航组件SideMenu

SideMenu组件相对于Layout.vue就相对复杂一些了,因为对于router.js中一些自定义信息的判断,都是在这里,代码如下:

 <Menu theme="dark" width="auto" :class="menuitemClasses">
    <template v-for="item in menuList">
      <template v-if="item.children && item.children.length === 1">
        <!--          只有一个children元素的情况-->
        <SideMenuItem v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></SideMenuItem>
        <MenuItem v-else :name="getNameOrHref(item,true)" :key="`menu-${item.children[0].name}`">
          <svg v-if="item.children[0].meta.iconfont" class="icon" aria-hidden="true">
            <use xlink:href="#icon-weixin"></use>
          </svg>
          <Icon v-else :type="item.children[0].meta.icon"></Icon>
          <span v-if="!isCollapsed" :class="{active:!isCollapsed}">{
    
    {
    
     showTitle(item.children[0]) }}</span>
        </MenuItem>
      </template>
      <template v-else>
        <!--          没有children或者有多个children的情况-->
        <SideMenuItem v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></SideMenuItem>
        <MenuItem v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`">
          <svg v-if="item.meta.iconfont" class="icon" aria-hidden="true">
            <use xlink:href="#icon-weixin"></use>
          </svg>
          <Icon v-else :type="item.meta.icon"></Icon>
          <span v-if="!isCollapsed" :class="{active:!isCollapsed}">{
    
    {
    
     showTitle(item) }}</span>
        </MenuItem>
      </template>
    </template>
  </Menu>

导航主要应用了iview框架的Menu组件。我们渲染导航,总得根据一个数组数据来渲染吧,这个数据从哪来的,没错,就是从router.js中获取的。

我的router数据传输过程大致为:

  1. 在pinia中定义获取router.js数据的getters方法,pinia是类似vuex的全局管理工具。
  2. 在Layout.vue中调用pinia,获取router.js中的数据定义为:menuList
  3. 通过父子组件传参,把menuList传递给sideMenu组件。
  4. sideMenu得到组件后,开始对它进行循环渲染,这就是上面的:v-for=“item in menuList”

在渲染过程中,可以通过item的一些自定义配置,来控制渲染效果,比如我的导航栏里,有以下几个处理:

  1. 根据节点item是否存在children对象,判断是否渲染二级导航栏。
  2. 我自定义了一个meta属性,里面会有一些自定义参数,比如我会在sideMenu中判断 icon来决定渲染的图标,根据title来判断渲染标题。

4. 实现跳转

Layout和SideMenu组件都讲解完毕,那是如何做到点击导航栏实现跳转的呢?

答案是,我在Layout组件中写入SideMenu时,定义了一个方法@on-select=“turnToPage_sideMenu”,当点击选择导航栏时,调用方法如下:

function turnToPage_sideMenu(name) {
    
    
  console.log(name)
  turnToPage(router, name)
}

//在另一个工具函数中定义了方法turnToPage
export const turnToPage= ($router,route)=> {
    
    
    let {
    
     name, params, query } = {
    
    }
    if (typeof route === 'string') name = route
    else {
    
    
        name = route.name
        params = route.params
        query = route.query
    }
    if (name.indexOf('isTurnByHref_') > -1) {
    
    
        window.open(name.split('_')[1])
        return
    }
    $router.push({
    
    
        name,
        params,
        query
    })
}

总结

这部分代码略多,没办法都粘贴上来,如果有兴趣详细了解的同学,可私聊或评论我获取源码。

根据路由信息渲染导航栏其实思路很简单:

  1. App.vue中定义router-view标签,作为路由入口
  2. 除了login等特殊组件,每个业务组件的父级component都定义一个Layout布局组件,这样每次跳转到不同组件,都有父组件Layout
  3. Layout布局组件中做两件事:一是SideMenu导航组件,二是定义router-view作为嵌套的子路由,用来渲染router.js中children里定义的组件。
  4. 在全局管理工具中获取router信息,如果不考虑后续,甚至可以不用全局管理工具,直接在需要的组件中获取即可。把menulist渲染到SideMenu导航组件
  5. 定义跳转方法turnToPage_sideMenu,点击导航栏时跳转到对应的组件。

猜你喜欢

转载自blog.csdn.net/zjsj_lize/article/details/128550496