vue动态标签路由跳转

长话短说,这里的路由的就是实现各个网页跳转的一个关键组件,下面我们来一步一步地解析一下路由的使用过程。

创建路由

首先我们在src文件夹下的router的index.js中创建我们需要的路由路径。

话不多说我们这就在index.js中开写!!

首先我们先引入必须的组件:

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Index from '../views/index.vue'

其中import { createRouter, createWebHistory } from 'vue-router'是不变的官方给的创建路由的组件,而 HomeViewIndex则是我们手动创建的组件,相信大家从from关键字后面的路径名称也能够看出来。

接下来我们就来创建一个路由吧~~

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
})

这个history创建了一个基于 HTML5 History API 的路由历史对象。这种类型的历史对象允许你构建一个单页应用程序(SPA),其中页面的导航不会导致页面重新加载,但是可以改变 URL 并允许用户通过浏览器的前进和后退按钮来导航。

而其中的import.meta.env.BASE_URL就是指定了根路径,根路径的默认值为/,看到/是不是突然就顿悟了??没错,我们的项目的主页路径http://localhost:5174/除去默认的http://localhost:5174之后那最后的/不就是我们import.meta.env.BASE_URL产生的嘛。换句话来说这段代码产生我们默认的首页路径。

既然我们首页路径都有了,那我们后续的网页路径不就在首页路径后面添加不就成啦?说干就干。我们继续来写~

const router = createRouter({
   routes:[
{
 path: '/',
      name: 'Index',
      component: Index,
      children: [
        {
          path: '/home/index',
          name: 'index',
          meta: { title: '首页' },
          component: () => import("../views/home/index.vue")
        },
}
]
})

上面这段代码就是在定义我们页面跳转路径和对应要跳转的组件名称了,上面这段长的其实最有用的就俩句代码:

 path: '/',
 component: Index,
        },

是的,我们要有有路由的路径以及要跳转的组件名称,那就足够了,至于其余的无伤大雅。

好,我们接着讲这个children是咋回事,我们直接翻译过来我们也大致可以猜出来了,children的意思是儿童,那不就是说是我们当前路由的一个子类嘛~。那我们当前处于/的路径状态下,那我们子类里面写了路径path: '/home/index'不就是说我们的子网页的路径为:/home/index嘛,对应跳转的组件则为 ../views/home/index.vue里面的网页。到这里就懂了吧?这些懂了就可以了,我们创建路由的事情就完成了。哦对了,我们最后要我们的router常量给export出去方便其余页面调用。

export default router

其余的部分按照上面的思路来就好了,接下来展示一下完整的代码:

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Index from '../views/index.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL), 
  routes: [
    {
      path: '/',
      name: 'Index',
      component: Index,
      children: [
        {
          path: '/home/index',
          name: 'index',
          meta: { title: '首页' },
          component: () => import("../views/home/index.vue")
        },
        {
          path: '/system',
          name: 'system',
          meta: { title: '系统管理' },
          children: [
            {
              path: '/system/log',
              name: 'log',
              meta: { title: '系统日志' },
              component: () => import("../views/system/log.vue")

            }

          ]
        },
        {
          path: '/user',
          name: 'user',
          meta: { title: '员工同事' },
          children: [
            {
              path: '/user/index',
              name: 'userindex',
              meta: { title: '员工管理' },
              component: () => import("../views/user/index.vue")
            }
          ]
        },
        {
          path: '/attendance',
          name: 'attendance',
          meta: {
            title: '考情统计'
          },
          children: [
            {
              path: '/attendance/info',
              name: 'attendanceindex',
              meta: { title: '个人考情' },
              component:()=>import("../views/info.vue")
            }
          ]

        },
        {
          path: '/device',
          name: 'device',
          meta: { title: '设备管理' },
          children: [
            {
              path: '/device/index',
              name: 'deviceinfo',
              meta: { title: '正在运行' },
              component:()=>import("../views/device/index.vue")
            }
          ]
        },
        {
          path: '/material',
          name: 'material',
          meta: { title: '物料管理' },
          children: [
            {
              path: '/material/index',
              name: 'materialinfo',
              meta: { title: '正在运行' },
              component:()=>import("../views/material/index.vue")
            }
          ]
        },
        {
          path: '/productoutbound',
          name: 'productoutbound',
          meta: { title: '产品数据' },
          children: [
            {
              path: '/productoutbound/index',
              name: 'productoutboundinfo',
              meta: { title: '产品出库' },
              component: () => import("../views/productoutbound/index.vue")
            },
            {
              path: '/productoutbound/info',
              name: 'productqualityinspection',
              meta: { title: '产品质检' },
              component:() => import("../views/productoutbound/info.vue")
            }

          ]
        },
        {
          path: '/analyse/info',
          name: 'Promotion',
          meta: { title: '统计分析' },
          component: () => import("../views/analyse/index.vue")
        },
      ]
    }
  ]
})

export default router

注册路由

注册路由很简单,我们路由创建好了之后总得让系统知道咱们在哪吧?欸,这时候我们在vue自带的文件main.js上写入:

import router from './router'
const app = createApp(App)
app.use(router)

上面的./router代表了我们刚刚上面创建路由导出的常量router的路径,然后我们使用vue框架自带的app.user(router)即可。这里app是干啥的就不讲了,大家去看看官网文档啥的,这个我的理解是和.NET里面的那个builder差不多的。

使用路由

接下来我们将通过一个for循环和结合我们的Element-plus UI框架来动态地加载我们的路由,实现效果图如下图所示:

vue动态标签路由跳转

接下来我们在将在components文件夹下创建组件navMenu.vuenavItem.vue

其中navMenu.vue封装了我们上图左侧导航栏部分的框架,也就是准备工作,而navItem.vue才是真正用for循环去实现导航栏具体内容部分。知道了这俩组件的作用,那我们话不多说直接开动!

实现navMenu.vue组件

首先是我们的navMenu.vue组件部分,这个肯定是得先写的,咱们得把大体框架写好才能继续下一步嘛~

<template>
  <el-aside width="200px" class="aside">
    <el-menu >
      <el-menu-item >
          <el-image />
      </el-menu-item>
      <NavItem  />
    </el-menu>
    </el-aside>
</template>

为了方便讲解,我剥离了大部分属于美观部分的代码,剩下一个框架部分的代码,我认为这个组件的核心部分应该是这个框架部分,懂了框架部分精华部分就学到了。接下来我们来逐步讲解一下。

首先最外层这个组件用一个组件包起来了,这里我们可以理解为这个组件就代表了整个我们的导航栏。

vue动态标签路由跳转

可以看到我们导航栏分成了图片log部分和具体的功能部分,所以我们又把整个导航分成了 ,图片log内容我们封装在了 ,而具体的功能按钮我们则封装在了里面。

那我怎么知道我们遍历出来的里面的每个导航按钮名字是我们所需要的呢?我们总得把对应的按钮名字和按钮路径传给给组件才行吧?那既然如此我们在navMenu.vue组件部分还得干一件事,我们得把每个导航按钮的名字和路径都给定义一下,那话不多说我们直接开干~~

<script setup>
import { reactive } from 'vue'

const items = reactive([
  {
    text: '首页',
    url: '/home/index',
    icon: 'HomeFilled'
  },
  {
    text: '系统管理',
    url: '/system',
    icon: 'Grid',
    child: [
      { text: '系统日志', url: '/system/log', icon: 'Setting' }
    ]
  },
  {
    text: '员工同事',
    url: '/user',
    icon: 'Avatar',
    child: [
      { text: '员工管理', url: '/user/index', icon: 'User' }
    ]
  },
  {
    text: '考勤统计',
    url: '',
    icon: 'StarFilled',
    child: [
      { text: '个人考勤', url: '/attendance/info', icon: 'User' },
      { text: '考勤分析', url: '/attendance/analyse', icon: 'More' },
    ]
  },
  {
    text: '设备管理',
    url: '/device',
    icon: 'HelpFilled',
    child: [
      { text: '正在运行', url: '/device/index', icon: 'Tools' }
    ]
  },
  {
    text: '物料管理',
    url: '/material',
    icon: 'Share',
    child: [
      { text: '物料仓库', url: '/material/index', icon: 'Share' }
    ]
  },
  {
    text: '产品数据',
    url: '/Promotion',
    icon: 'Promotion',
    child: [
      { text: '产品出库', url: '/productoutbound/index', icon: 'Promotion' },
      { text: '产品质检', url: '/productoutbound/info', icon: 'Promotion' }
    ]
  },
  {
    text: '统计分析',
    url: '/analyse/info',
    icon: 'VideoCameraFilled'
  }
])
</script>

这里解释下我们引入reactive函数的作用,这个函数的作用在于假如我们items里面的内容在运行过程改变了,那么reactive函数会保证后续其余调用了我们items部分的内容同步更新。那有人说这有啥用,我们导航栏内容肯定不变的,要改变我们直接改代码就好了呀。是的这句话的确没错,但是我们要考虑的全面一点,假如我们导航栏的内容显示是根据用户权限来的呢?系统管理的内容权限肯定要比普通用户多呀,此时我们reactive就可以保证我们系统权限变成管理员的时候对应导航栏的内容也同步改变。

我们items常量其实就是一个数组,每个列表{}封装一个导航栏内容,导航栏内容由texturlicon组成。text则显示导航栏按钮的名字,url则是对应导航栏所要跳转的路径,icon则是导航栏按钮的图标log。

至此一切基础准备已经就绪了,接下来我将对应的数据填入<template>部分:

<template>
  <el-aside width="200px" class="aside">
    <el-menu :default-active="route.path" exact class="el-menu-vertical-demo" router>
      <el-menu-item>
          <el-image />
      </el-menu-item>
      <NavItem v-for="v in items" :key="v.url" :item="v" :basePath="v.url" />
    </el-menu>
  </el-aside>
</template>

这里核心部分就是一个<el-menu :default-active="route.path" exact class="el-menu-vertical-demo" router><NavItem v-for="v in items" :key="v.url" :item="v" :basePath="v.url" />

其中<el-menu :default-active="route.path" exact class="el-menu-vertical-demo" router>的参数:

  • default-active="route.path":

    本质是一个动态绑定属性,它的作用是设置菜单(Menu)组件当前激活的菜单项。这里绑定的route.path通常来自Vue Router,表示当前路由的路径。这样设置可以确保当前访问的路由对应的菜单项被高亮显示。

  • exact:

    在 Vue.js 的 el-menu 组件中,exact 属性用于控制路由的激活状态。具体来说,exact 属性确保只有当路由路径与菜单项的 index 完全匹配时,该菜单项才会被激活。

    默认情况下,Vue Router 的路由匹配是基于前缀的。这意味着如果当前路由是 /home,那么所有以 /home 开头的路由(例如 /home/index)都会被认为是匹配的。这可能导致多个菜单项同时被激活。

    通过设置 exact 属性,你可以确保只有当路由路径与菜单项的 index 完全匹配时,该菜单项才会被激活。这对于避免多个菜单项同时被激活的情况非常有用。

  • router

    router 属性用于启用菜单项与 Vue Router 的集成,使得菜单项可以作为导航链接使用,简单来说就是告诉菜单我是通过路由进行跳转的。

    以下是详细解释:

    • el-menu 组件设置了 router 属性后,每个 el-menu-itemindex 属性会被视为一个路由路径。
    • 当用户点击某个 el-menu-item 时,el-menu 组件会自动调用 Vue Router 的导航功能,将应用导航到 index 属性指定的路径。

第一个重点我们就讲完了,接下来我们讲解一下<NavItem v-for="v in items" :key="v.url" :item="v" :basePath="v.url" />,先说明一下,这里的<NavItem />可不是vue框架自带的一个组件哦~这个其实就是咱们的navItem.vue组件,我们通过`import xxx from xx将其导入:

<script setup>
import { reactive } from 'vue'
import { useRouter, useRoute } from "vue-router";  //导入路由
import NavItem from './navItem.vue'  //子组件  
import langlangfactoryImage from '../assets/images/langlangfactory.png'

const route = useRoute();
</script>

oh~忘记讲了,我们上面<el-menu :default-active="route.path" exact class="el-menu-vertical-demo" router>用到的router属性也算从上面这段代码里面导入的哦~~

我们继续讲解<NavItem v-for="v in items" :key="v.url" :item="v" :basePath="v.url" />

  • v-for="v in items"

    这里我们通过v-for 进行遍历items,这个items就是我们上面定义的数组,以每个{}为单位作为一个v传给组件。

  • :key="v.url"

    :key是vue自带的一个属性,它的主要作用是帮助 Vue 识别和跟踪每个节点的唯一性。当列表数据发生变化时,Vue 会根据 key 属性来重新排列元素,以确保 DOM 更新尽可能高效。例如:一个列表从 [A, B, C] 变为 [B, A, C],Vue 会根据 key 值来移动 AB 的位置,而不是重新创建它们。

  • :item="v" :basePath="v.url"

    这个item 和basePath就不同于key了,这俩是我们自己创建的遍历,item存储我们遍历出来items数组里面的{}数据,basePath存储{}里面的url字段的内容,也就是我们导航按钮的路由路径。

到这里我们navMenu.vue组件的按钮我们就讲解结束了,接下来我们讲解navItem.vue的组件内容,也就是我们要正式去实现我们的组件啦!!!


实现navItem.vue组件

首先明确我们navItem.vue组件的作用,其作用就是实现具体的导航按钮的加载,我们再来回顾一下我们的导航栏效果吧~

vue动态标签路由跳转

发现没有,我们导航栏有些只有一个单独一个导航按钮,有些则是导航按钮下面还有一个子按钮,这就说明了,我们navItem.vue里面要分成有子级导航按钮和无子级导航按钮的区别,所以我们代码如下:

<template>
  <!-- 无子级 -->
  <el-menu-item :index="basePath" v-if="!item.child || item.child.length <= 0">
   <el-icon>
        <component :is="item.icon"></component>
     </el-icon>
     <span>{{ item.text }}</span>

  </el-menu-item>
  <!-- 有子级 -->
  <el-sub-menu  :index="basePath" v-else>

    <template #title>
        <el-icon>
           <component :is="item.icon"></component>
        </el-icon>
        <span>{{ item.text }}</span>
     </template>

     <NavItem v-for="sub in item.child" :key="sub.url" :item="sub" :basePath="sub.url" />
  </el-sub-menu>
</template>

<script setup>
import { ref, reactive } from 'vue'
const { item, basePath } = defineProps({
  item: {
     type: Object,
  },
  basePath: {
     type: String
  }
})

这里的代码我依旧是剥离了一些美化样式的代码,只保留的核心的代码,这样可以看的更加清晰一点,更加能够把握住重点。

在此之前我们要介绍一下<script setup>部分的代码的,这里我们用defineProps函数引入了itembasePath,这个defineProps的作用其实就是类似于定义了一个构造函数,里面接收了itembasePath这俩参数,这俩参数从何而来呢?这俩参数当然是我们在前面navMenu.vue组件中 使用 <NavItem v-for="v in items" :key="v.url" :item="v" :basePath="v.url" />传给当前组件navItem.vue的呀,这样我们在navItem.vue就能使用那些在navMenu.vue中传入的数据了。

回到我们navItem.vue的代码呢,首先我们是利用了v-ifv-else去判定我们的导航按钮是否有子级,这里的v-ifv-else和我们普通的if语句其实是一样的,当我们不满足v-if="!item.child || item.child.length <= 0"的时候也就是说我们当前的导航栏按钮无子级的时候我们执行下面v-else那条语句,否则我们执行v-if这条语句。

接下来我们介绍一下上面我们代码用到的<el-menu-item />

  • `

    这个就是无子级导航菜单的实现,他的内容部分你直接给其写上标签名词和图片log即可。

    <el-menu-item :index="basePath" v-if="!item.child || item.child.length <= 0">
     <el-icon>
          <component :is="item.icon"></component>
       </el-icon>
       <span>{{ item.text }}</span>
    </el-menu-item>

    这里的index="basePath"还是一样的,就是为了当路由路径与菜单项的 index 完全匹配时,该菜单项才会被激活,主打的一个就是告诉路由器我要去basePath这个路径,但是得等路由器确认它能够到达才可以。

  • 这个呢就是有子级导航菜单的实现组件,这个你可以把他分为两个部分,一个部分是标题,另一个部分是内容。下面我来用一幅图来解释一下它的这俩的组成。

    vue动态标签路由跳转

上图就是一个有子级标题的导航栏,现在你是不是一目了然了?对应子级导航菜单来说父级的按钮就是标题,子级部分的按钮就是内容。

那么直接来看这部分的代码:

  <template #title>
        <el-icon>
           <component :is="item.icon"></component>
        </el-icon>
        <span>{{ item.text }}</span>
     </template>

以上是标题部分的代码,这里的#title是必须添加的,这是内置的一个参数,用于将item那些参数传给标题,这样就能调用上面的item.iconitem.text了,否则是无法调用的。

     <NavItem v-for="sub in item.child" :key="sub.url" :item="sub" :basePath="sub.url" />

这部分则是内容部分,采用了递归调用自身的方式来加载出我们的子导航栏,这个就是当前组件的名字navItem.vue,因为window系统是不区别小写的,所以写成是一样的。这样调用自身之后再进行渲染以实现子导航栏的出现。

这种操作是不是非常秒呢,如果不懂这个点,仔细想想递归算法的作用,然后代入试一下你就明白啦~~。

到这里我们总结一下路由router和:index的作用

  • :index

    显示网页中的url路径,也就是说:index决定的以下这一部分能被我们肉眼看见的路径,但是到底能不能去这个网页不清楚,这个得看router的脸色。

    vue动态标签路由跳转

  • router

    当得知:index想要去上图路径的时候,它会去看看创建路由的时候列表(一开始我们router文件夹里面的index.js的内容)到底有没有我们上图对应的路径,如果是有的,那直接跳转到对应的页面,如果没有那啥也不显示。

以上就是本人对vue框架路由跳转组件的学习理解,如有错误请多包涵~~~~。