1

写在前头

由于搭建后台管理系统,国内多采用侧边栏+多标签切换页面的方式,故在实现中同样打算做一个多标签页的组件。

↓↓↓↓↓这玩意就是标签栏,我个人习惯称为tabs

示例图

需求描述

  • 点击侧边栏的某个item,判断该页面是否已经打开过

    • 没打开过:

      • vue-router自动载入组件,进入页面
      • 我们代码控制,在tabs的数组添加当前新增的这个页面的路由信息
    • 打开过,切换到对应的tab-item和页面
  • 点击不同的tab-item实现,页面的切换(这一步是vue-router自动完成的)
  • 每个tab-item可以单独关闭(首页不可关闭)
  • 右键tabs栏目,显示context-menu

    • 可关闭当前:同直接在tab-item上关闭
    • 关闭其他:保留当前页和首页
    • 关闭全部:保留首页
  • 默认关闭当前tab-item和对应的页面后,自动跳到前一个tab-item和页面

遇到的问题

这里暂时记录当前遇到的问题,后续若有新坑,再补录。。

pppp.jpeg

关闭当前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数组里删除掉那个tab-item

理想的结果:

  • 依次点击t1、t2、t3、t4,分别打开对应的页面,并在tabs上增加tab-item
  • 点击tab-item[t4]的关闭按钮,tabs栏目变为[t1,t2,t3],页面自动更新到t3对应的页面

实际上:

  • 点击tab-item[t4]的关闭按钮
  • 地址栏一瞬间跳到了t3对应的路由
  • 紧接着又跳回了t4对应的路由
  • watchroute的两次变化,且在第二的变化中重新在tabs的数组中新增了t4
  • 最终tabs[t1,t2,t3,t4],死循环...

排查思路

  • 首先想,会不会是代码逻辑的问题,反复确认,并单独写了个简化版的demo后,确定逻辑没有问题
  • 然后想到会不会是vuetify的tabs组件有一些内部的逻辑导致的

    • google搜索了一下,没有搜到相关问题(主要怀疑在optional这个属性)
    • 然后又去别人的项目中看了一下,同样使用的tabs也没有问题
  • 在参考品别人项目的时候,看到他们都是讲逻辑卸载vuex的action

    • 这时候我想 难道是我直接调用mutation出现的问题?
    • 虽说action支持异步,mutation只能是同步;但是我的router.push逻辑就是写在mutation删除当前tab-item之后执行的啊,不可能出现问题啊
  • 然后想会不会是我点击关闭按钮的时候,又触发了tab-item的点击(close按钮是包裹在tab标签里),事件向上冒泡导致重新载入了当前页呢?

    • 然后我将关闭当前tab-item按钮的点击事件改为@click.stop="onTabClose"
    • 发现然并卵
    • 就排除了这种可能
  • 最后,在不停的对照别人的项目过程中,发现别人的点击事件是@click.stop.prevent="onTabClose"

    • 尝试着增加了.prevent
    • 果然好了,卧槽!!!!!

心里顿时一万个草尼玛飘过~~~

456.jpg

在骂自己为什么不多好好看几遍文档的时候,突然意识到,这TM概念我其实知道啊,但是.prevent不是阻止事件默认行为的么,劳资实在想不到在这用啊。。。。。。


本着人道主义精神,不能光骂娘,不解决问题啊,于是有了如下:

拨开云雾见光明:

  • 首先,打开了浏览器调试控制台,通过Vue-DevToos发现,Vuetify的Tab本质上就是一个router-link

image.png

  • router-link的咋就导致出现问题了呢?直接一步到位看看生成的dom是啥吧

image.png

  • 这个是意料之中,就是生成一个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标签的默认行为

qqqqqqq.jpeg

你说谁TM能想到啊😭,吃一堑长一智把,以后再遇到类似场景的问题,一定要得多注意!!!

写在结尾

  • 这个问题,折磨我两天半了,终于也算了却了一桩心愿。。
  • 我这个后端临时干前端,果然是遭受了慢慢的恶意
  • 不过还是怨自己没有扎实的前端基本功,后面一定得好好系统学习一下前端知识了
  • 以上。

xxinyue
7 声望3 粉丝

欲求木之长者,必固其根;欲求流之远者,必浚其源!