Stevv's Blog

Do not go gentle into that good night

Vue3如何实现动态面包屑

面包屑的介绍和实现思路

首先我们先来了解一下面包屑功能,这个功能其实就是根据路由的地址,来动态的生成导航菜单,今后在路由发生变化的时候,这个导航菜单也会动态的发生变化,就像下面展示的这样:

那么像这样的功能我们该如何实现呢?

  • 对于这个面包屑来说,他的静态版本我们可以直接在element-plus上面直接复制粘贴即可,这个不做过多的赘述

  • 我们要实现的版本是动态的进行渲染,那么这个就需要我们获取相应路由的数据结构(比如说数组),该如何获取相应的路由数据呢?

因此完成面包屑组件的关键就是在于获取相应路由数据上面,这就是典型的数据驱动

获取路由数据

我们要实现这个功能就需要用到route.matched,代码如下

1
2
3
//首先实例化获取当前路由
const route=useRoute()
//然后调用route.matched

对route.matched的理解

首先我们来看官方给出的解释:

官方解释: 与给定路由地址匹配的标准化的路由记录数组。

个人理解:我个人的理解就是从根据你访问的路由,然后向上找,找到当前路由的根路由然后返回一个数组,这个数组包含的就是你当前访问的路由加上根路由,可能这么说你还是不是很理解,举个例子:

1
2
3
4
5
6
7
8
9
const router=[{
 path: "/user",
 component: Layout
 children: [
  {path: "/user/manage", component:Manage },
  {path: "/user/info/:id", component:userInfo   },
  {path: "/user/import",  component:import },
],
}]

这里不做路由的细节展示,只是为了说明一个例子,如果我们访问/user/manage这个地址的时候,此时如果我们使用route.matched打印结果会得到如下结果:

1
2
3
[  { path: '/user', component: Layout,children:[...所有的子路由] },
{ path: '/user/manage', component: Manage }
]

这样我们就能根据获取的数组来进行面包屑的渲染了。

开始完成动态面包屑

在进行完前期的铺垫之后,我们就可以开始完成动态的面包屑了

封装面包屑组件:

我们查看element-plus官网的相关信息之后,发现要实现面包屑的功能需要使用这两个组件

  • el-breadcrumb用来包裹所有子项

  • el-breadcrumb-item这是每个子项


    首先是布局部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
 <div>
   <el-breadcrumb class="breadcrumb" separator="/">
     <transition-group name="breadcrumb">
       <el-breadcrumb-item
         v-for="(item, index) in breadcrumbData"
         :key="item.path"
       >
         <!-- 不可以点击项 -->
         <span
           v-if="index === breadcrumbData.length - 1"
           class="no-redirect"
           >{{ item.meta.title }}</span
         >
         <!-- 可以点击项 -->
         <a v-else class="redirect" @click.prevent="onLinkClick(item)">{{
          item.meta.title
        }}</a>
       </el-breadcrumb-item>
     </transition-group>
   </el-breadcrumb>
 </div>
</template>

第一部分的代码就是根据获取的路由数据进行面包屑的渲染,使用了v-for来进行el-breadcrumb-item的渲染,我们在这里规定了数组的最后一项是不可被点击的,并且给它指定了样式,然后其他项就可以被点击,并且绑定了点击事件,将渲染的item传递过去,我们重点来讲解一下js这部分的代码。

Vue部分的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script setup>
import { ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useStore } from "vuex";
const route = useRoute();
const router = useRouter();
// 生成数组数据
const breadcrumbData = ref([]);
const getBreadcrumbData = () => {
 console.log(route.matched);
 breadcrumbData.value = route.matched.filter((item) => {
   return item.meta && item.meta.title;
});
};
// 监听路由发生改变时触发
watch(
 route,
() => {
   getBreadcrumbData();
},
{
   immediate: true,
}
);
// 处理点击事件
const onLinkClick = (item) => {
 router.push(item.path);
};
// 如果将来需要进行主题替换,所以这里获取下动态样式
const store = useStore();
// eslint-disable-next-line
const linkHoverColor = ref(store.getters.globalCss.breadCrumbColor);
</script>
  • 首先我们先定义了一个数组,这个数组用来保存要渲染的面包屑数据项

  • 接着我们定义了一个函数,这个函数是为了获取当前的路由项和在与给定路由匹配的根路由和这个路由,然后过滤一下数据项,根据数据项中是否有title和meta对象来过滤数据,最终返回一个数组,里面就是面包屑要展示的数据,其实和侧边栏数据是对应的

  • 我们这里还是用了watch来监测route的数据的变化,每当数据发生变化时候就调用getBreadcrumbData这个方法,重新给数组赋值

  • 紧接着我定义了一个点击事件,每当用户点击之后相应的子项的时候,我们就会跳转到相应的页面。

  • 我们这里还是使用vuex,这个是为了之后项目可能会使用全局更换颜色(换肤功能),所以这里使用了全局的数据共享,根据Vue3新增的特性,我们可以在css中使用这个变量,当vuex中的数据改变,那么样式就会进行相应改变。


    动画样式的完成

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    css
    复制代码.breadcrumb-enter-active,
    .breadcrumb-leave-active {
    transition: all 0.5s;
    }

    .breadcrumb-enter-from,
    .breadcrumb-leave-active {
    opacity: 0;
    transform: translateX(20px);
    }

    .breadcrumb-leave-active {
    position: absolute;
    }

    基本的样式的完成:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    .breadcrumb {
     display: flex;
     font-size: 14px;
     line-height: 50px;
     margin-left: 8px;
     .no-redirect {
       color: #97a8be;
       cursor: text;
    }
     .redirect {
       color: #666;
       font-weight: 600;
    }
     .redirect:hover {
       // 将来需要进行主题替换,所以这里不去写死样式,这个是vue3的新增特性
       color: v-bind(linkHoverColor);
    }
    }