背景
最近在写后台项目时,使用RouterLink组件去实现如下图所示的顶部标签栏时,遇到了跳转失败的情况,因此记录一笔。
问题复现
具体实现标签栏的代码如下:
<el-scrollbar ref="elScrollbar">
<div class="tag-view-container">
<router-link
class="tag-view__item"
v-for="(item, index) in tagViewList"
:key="index"
:to="item.path"
@click="toScrollCurrentTagView()"
>
<div class="tag_view__body">
<span class="tag_view_title">{{ item.meta.title }}</span>
<el-icon
v-if="isAffix(item)"
class="icon-close"
@click="handleCloseTagView(item)"
>
<Close />
</el-icon>
</div>
</router-link>
</div>
</el-scrollbar>
这里可以看到,我通过在tag_view__body
外面包裹一层RouteLink组件,实现点击跳转到相应的路由。标签栏的基本操作主要新增、删除.具体实现,我也贴出代码供大家参考:
// 新增标签的实现
import { useTagViewsStore } from '@/store/modules/tagViews'
watch(route, () => {
tagViewsStore.addTagView(Object.assign({}, route))
setCurrentTagView() // 用来设置当前路由激活状态
})
新增标签我这里采用的是通过watch
监听路由的变化,如果发送变化就调用pinia中的设置的方法将当前路由加入tagViewList
(标签集合)中。这里需要注意的是对于route需要进行一次拷贝,否则你会发现,每次添加新的标签到tagViewList
集合中时,标签集合中的所有数据都和最后一次添加的数据一样,主要原因是存储的是引用类型的地址,如果没有进行拷贝的话,每次传入的都是同一个对象的地址,导致最后获取tagViewList
中的元素都是和最后一次添加的相同。至此,新增操作没有太大的问题。
先贴出删除标签的操作代码:
// 关闭标签操作
function handleCloseTagView(tagView, event) {
event.stopPropagation()
// findTagViewIndex() 返回当前关闭标签在tagViewList中的index
const tagViewIndex = findTagViewIndex(tagViewList.value, tagView)
const lastIndex = tagViewIndex - 1
// isActive() 方法是用来判断当前关闭是否是已激活的标签,如果是激活的标签需要指定下一个激活的标签,如果不是则直接删除
if (isActive(tagView)) {
router.push({ path: tagViewList.value[lastIndex].fullPath })
}
tagViewsStore.deleteTagView(tagView)
}
写到这里,我以为删除部分已经处理完了,easy~ 可是理想很丰满,现实却很骨感。页面实现的效果如下所示:
这里我点击了关闭角色管理标签,可以看到角色标签并没有关闭。正确的关闭执行情况应该是:角色管理标签关闭,用户管理激活,路由跳转到用户管理。这里我在全局前置路由守卫中打印了回调函数中的
to
和from
,发现路由从角色管理调转到了用户管理,但是又从用户管理跳回了角色管理(由于在前面标签新增那里使用watch
监听了路由的变化,因此当路由从用户管理跳回角色管理的时候,又向tagViewList
中添加了角色管理的标签)。
究竟是什么原因引起的路由跳转了两次呢?
路由多次跳转原因
后来经过万能的百度发现,使用RouterLink组件内嵌元素身上的绑定的事件会触发RouterLink组件的默认行为(触发路由跳转),这就解释了为什么路由发生了两次跳转。
第一次是通过标签的点击事件触发调用router.push()
进行跳转的,第二次是因为触发了RouterLink自身的默认行为(跳转的路由正好是RouterLink组件to
属性对应的路由,这里点击的是角色管理,路由从原本手动跳转到的用户管理,又二次跳转到角色管理)。
处理方法
弄明白为啥路由跳转两次后,处理这个问题就简单多了。究其根本都是因为触发了RouterLink组件本身的默认行为,所以只要阻止默认行为就可以了。这时候聪明的小伙伴已经想到了vue提供的prevent
修饰符,只要内嵌元素的绑定事件上使用prevent
修饰符,就可以阻止RouterLink组件默认行为的触发啦~~
最后
很少写输出文章,写作文笔有不好之处或知识点有误的地方还请各位看官多多包涵,如果有什么建议和想法也可以在评论区留言!最后创作不易,如果有所帮助请记得点赞呦~~ 🖊 ♥