写在前头
由于搭建后台管理系统,国内多采用侧边栏+多标签切换页面的方式,故在实现中同样打算做一个多标签页的组件。
↓↓↓↓↓这玩意就是标签栏,我个人习惯称为tabs
需求描述
-
点击侧边栏的某个item,判断该页面是否已经打开过
-
没打开过:
-
vue-router
自动载入组件,进入页面 - 我们代码控制,在tabs的数组添加当前新增的这个页面的路由信息
-
- 打开过,切换到对应的tab-item和页面
-
- 点击不同的
tab-item
实现,页面的切换(这一步是vue-router
自动完成的) - 每个
tab-item
可以单独关闭(首页不可关闭) -
右键tabs栏目,显示
context-menu
- 可关闭当前:同直接在tab-item上关闭
- 关闭其他:保留当前页和首页
- 关闭全部:保留首页
- 默认关闭当前tab-item和对应的页面后,自动跳到前一个tab-item和页面
遇到的问题
这里暂时记录当前遇到的问题,后续若有新坑,再补录。。
关闭当前tab-item
在基本实现了点击sidebar-item打开页面,并在tabs数组中添加新的项后,开始做关闭当前tab-item
的功能,但是遇到了坑(灬ꈍ ꈍ灬)。。。
实现思路:
-
watch $route
的变化,如果新增的tab-item
在tabs数组(保存在vuex中)中不存在,就addTabItem()
- 点击
tab-item
的关闭按钮时,首先判断关闭的tab-item
是不是当前的路由 -
如果是:
- 从tabs的数组里删除掉当前的
tab-item
- 判断前一个
tab-item
是否存在 - 如果存在就
router.push
到前一个tab-item
- 从tabs的数组里删除掉当前的
-
如果不是:
- 直接从tabs数组里删除掉那个
tab-item
- 直接从tabs数组里删除掉那个
理想的结果:
- 依次点击
t1、t2、t3、t4
,分别打开对应的页面,并在tabs上增加tab-item - 点击
tab-item[t4]
的关闭按钮,tabs栏目变为[t1,t2,t3]
,页面自动更新到t3对应的页面
实际上:
- 点击
tab-item[t4]
的关闭按钮 - 地址栏一瞬间跳到了t3对应的路由
- 紧接着又跳回了t4对应的路由
-
watch
到route
的两次变化,且在第二的变化中重新在tabs的数组中新增了t4 - 最终
tabs[t1,t2,t3,t4]
,死循环...
排查思路
- 首先想,会不会是代码逻辑的问题,反复确认,并单独写了个简化版的demo后,确定逻辑没有问题
-
然后想到会不会是vuetify的tabs组件有一些内部的逻辑导致的
- google搜索了一下,没有搜到相关问题(主要怀疑在
optional
这个属性) - 然后又去别人的项目中看了一下,同样使用的tabs也没有问题
- google搜索了一下,没有搜到相关问题(主要怀疑在
-
在参考品别人项目的时候,看到他们都是讲逻辑卸载vuex的
action
中- 这时候我想 难道是我直接调用
mutation
出现的问题? - 虽说
action
支持异步,mutation
只能是同步;但是我的router.push
逻辑就是写在mutation
删除当前tab-item
之后执行的啊,不可能出现问题啊
- 这时候我想 难道是我直接调用
-
然后想会不会是我点击关闭按钮的时候,又触发了tab-item的点击(close按钮是包裹在tab标签里),事件向上冒泡导致重新载入了当前页呢?
- 然后我将关闭当前
tab-item
按钮的点击事件改为@click.stop="onTabClose"
- 发现然并卵
- 就排除了这种可能
- 然后我将关闭当前
-
最后,在不停的对照别人的项目过程中,发现别人的点击事件是
@click.stop.prevent="onTabClose"
- 尝试着增加了
.prevent
- 果然好了,卧槽!!!!!
- 尝试着增加了
心里顿时一万个草尼玛飘过~~~
在骂自己为什么不多好好看几遍文档的时候,突然意识到,这TM概念我其实知道啊,但是.prevent不是阻止事件默认行为的么,劳资实在想不到在这用啊。。。。。。
本着人道主义精神,不能光骂娘,不解决问题啊,于是有了如下:
拨开云雾见光明:
- 首先,打开了浏览器调试控制台,通过
Vue-DevToos
发现,Vuetify的Tab本质上就是一个router-link
- 那
router-link
的咋就导致出现问题了呢?直接一步到位看看生成的dom是啥吧
- 这个是意料之中,就是生成一个a标签,然后在标签内包裹着关闭按钮
- 那么问题看样子就出在这了,既然通过
.prevent
取消事件的默认行为来解决问题,那指定是a标签的默认行为不守妇道了 - google了一下,a标签及其子元素,默认的行为就是跳转进入他的
href
属性指向的链接 - 所以为了一探究竟,专门写了下面这个demo:
<!DOCTYPE html>
<html lang\="en">
<head>
<meta charset\="UTF-8" />
<meta name\="viewport" content\="width=device-width, initial-scale=1.0" />
<meta http-equiv\="X-UA-Compatible" content\="ie=edge" />
<title>a标签事件测试</title>
</head>
<body>
<a
id\="waicenga"
href\="[http://www.baidu.com](http://www.baidu.com)"
style\="width: 400px;height:400px;background-color:bisque;display: block"
onclick\="awaiceng(event)"
\>
<button
style\="width: 200px;height:200px;background-color:red"
onclick\="anniu(event)"
\>
这是按钮
</button>
这是外层
</a>
</body>
<script>
// 通过操作dom 监听click事件,来取消a标签的默认行为
document
.querySelector('#waicenga')
.addEventListener('click', function(event) {
// 通过操作dom添加的事件 优先级低于 直接在元素上绑定的onclick
// '外层div事件' -> '测试优先级'
alert('测试优先级')
event.preventDefault()
})
// 通过添加event.preventDefault()可以阻止a标签的默认行为
// 但是确点是在点击事件中对a标签默认行为进行的取消
// 通过测试发现:当在anniu()中阻止了冒泡,导致awaiceng()执行不到,进而导致event.preventDefault()没生效,所以a标签依然进行了跳转
// 所以我们
function awaiceng() {
alert('外层div事件')
// event.preventDefault()
}
/\*\*
\*通过测试发现
\* 1. 没有阻止点击事件冒泡的情况下
\* 执行序列: anniu() -> awaiceng() -> 跳转到百度
\* 2. 添加了事件冒泡后:
\* 执行序列:anniu() -> 跳转到百度
\*
\* 小结:a标签内放置的子元素,即使设置了阻止事件冒泡,也不能避免a标签的默认行为!!!!!!
\*
\*
\*/
function anniu(event) {
alert('里面的按钮的事件')
// event.stopPropagation()
// 在这里直接阻止a标签默认行为也是可以的
// event.preventDefault()
}
</script>
</html>
小结
-
.stop
确实是阻止了冒泡 - 但是本质上tab内部包裹了一个
router-link
(其本质是个a标签) - 所以虽然阻止了冒泡,但是a标签的默认行为是打开href链接
- 即使阻止了内部的冒泡,也然并卵
- 所以最重要的是阻止a标签的默认行为
你说谁TM能想到啊😭,吃一堑长一智把,以后再遇到类似场景的问题,一定要得多注意!!!
写在结尾
- 这个问题,折磨我两天半了,终于也算了却了一桩心愿。。
- 我这个后端临时干前端,果然是遭受了慢慢的恶意
- 不过还是怨自己没有扎实的前端基本功,后面一定得好好系统学习一下前端知识了
- 以上。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。