【论术】重构动态面包屑导航的实践
一人行,天地为师。—— 佚名
背景:项目舍弃了之前选择三级菜单跳转的方式,转而使用在大屏页跳转至各个模块的办法进行路由跳转,然而面包屑组件却依然使用的之前三级菜单的模式,由于菜单栏已被舍弃,因而只能使用浏览器后退的方式进行菜单选择,为了解决这个问题,因而有了使用面包屑进行菜单导航的需求。
为了实现这个需求,因而有了两种解决方法:
-
重构路由逻辑,重新规整IAM菜单并重新配置路由菜单逻辑
-
仅重构面包屑逻辑,根据路由的前进后退进行手动模拟。
结论:方法1是治标且治本的。但当前项目已成规模,项目架构最初始便是三级layout,相比较下时间成本耗费巨大,故而在权衡下放弃方法1.
使用方法2。
既然决定使用方法2,即使用路由模拟的办法维护面包屑列表,为了需求的可行性,则要穷举面包屑各种路由切换时的各种可能并给出对应的解决办法。
面包屑数据应在全局数据中维护,使用了pinia维护
为了规避浏览器刷新导致的数据清空,将其加入到持久化数组中:
//pinia
state: () => ({
// public breadcrumbs
breadcrumbList: [] // 公有面包屑数据
}),
persist: {
key: 'user',
paths: [
'breadcrumbList' // 将其加入持久化数组中
]
}
具体步骤:
思路:目前项目入口是首页->模块大屏->模块,因而可以在进入大屏时清空breadcrumbList列表,随即加入此大屏路径,然后将子模块依次推入面包屑数组中。
全局路由守卫:
// 路由守卫
router.beforeEach(async (to, from, next) => {
// other code...
// 在进入首页且面包屑内有数据时,清空面包屑数据
if (to.path === `/dashboard/base` && userStore.breadcrumbList.length > 0) {
userStore.clearBreadcrumbList()
}
// 在进入模块大屏且面包屑内有数据时,清空面包屑数据,随即加入大屏模块路径,否则push当前模块路径到面包屑组
if (to.path === '/outlink' && userStore.breadcrumbList.length > 0) {
userStore.clearBreadcrumbList()
userStore.pushBreadcrumbList({
name: `智慧审计赋能中枢`,
path: to.path
})
} else {
userStore.pushBreadcrumbList({
name: (to.name as string) || (to.meta.title as string),
path: to.path
})
// other code...
})
需求:面包屑组件子项除当前页面外均可以跳转,
遇到的问题:浏览器后退行为时重复添加,
因而在加入面包屑时要进行二次检验其是否已经存在于面包屑数组内:
// pinia
actions: {
// 将当前数据推入到面包屑数组中
pushBreadcrumbList(item: { name: string; path: string }) {
const itemIndex = this.breadcrumbList.findIndex((it) => it.path === item.path)
// 如果有索引,则将其后所有的路径全部清除,且中止此次操作
// 需求:面包屑点击事件
if (itemIndex > -1) {
console.log(`如果有索引`, item)
this.breadcrumbList = this.breadcrumbList.filter((it, idx) => idx <= itemIndex)
} else {
// 否则,正常添加
this.breadcrumbList.push({
name: item.name,
path: item.path
})
}
},
// 清空面包屑数组
clearBreadcrumbList() {
console.log(`执行清空面包屑数组函数`)
this.breadcrumbList.length = 0
this.breadcrumbList = []
},
}
PS: 为了规避浏览器后退时导致的可能的面包屑重复添加问题,有尝试过监听浏览器后退行为,但是并没有使用到,这是由于路由守卫可以监听route实例的变化,在这里操作便有些画蛇添足。遂放弃此种方法,不过这个方法是可用的,记录一下:
// app.vue
// 监听浏览器后退行为
const handlePopState = () => {
console.log('111111 :>后退> ', 111111)
//TODO: store.removeCurrentPage...
}
onMounted(() => {
window.addEventListener('popstate', handlePopState)
})
面包屑组件内容:
// src\layouts\components\Breadcrumb.vue
// 需求:当前页面不可点击。
<t-breadcrumb :max-item-width="'150'" class="tdesign-breadcrumb">
<t-breadcrumbItem
v-for="(item, index) in breadcrumbList"
:key="item.to"
:disabled="breadcrumbList.length - 1 === index"
:to="item.to"
@click="handleClickBreadcrumbItem(item, index)"
>
{{ item.name }}
</t-breadcrumbItem>
<template #separator> / </template>
</t-breadcrumb>
//
import { useUserStore } from '@/store'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { breadcrumbList } = storeToRefs(userStore)
// 点击面包屑时replace当前路由
const handleClickBreadcrumbItem = (item, index) => {
router.replace({
path: item.path
})
}
总结:
页面跳转行为由路由守卫执行,产生的面包屑数据由pinia维护, 在加入到面包屑数据时要判断是否存在,这是为了规避浏览器后退行为以及其它可能的添加重复路由的行为,如果已经存在,则剔除当前路由之后所有的模块路径(点击上一层面包屑数据时)。否则才执行添加动作。
以上。
浙公网安备 33010602011771号