这是一个悲伤的故事。某日清晨,距离版本转测还剩一天,切图仔的我正按照计划有条不紊的画页面。当我点击一个下拉弹框组件中分页组件页数过多而出现的向后 5 页省略号时,悲剧开始了,弹框被收回了。情景再现
javascript
问题的表象很简单,使用的是组件库的下拉弹窗组件,在组件中使用到了分页组件,当点击分页组件的向后 5 页快速跳转时,弹窗被收回了。咱们的预期是可以继续操做的,只有点击弹框外部时,弹窗才会被收回。vue
发现这个问题我作了以下分析:java
肯定这是一个问题
再次重复操做问题,肯定问题出现的条件,可以在特定条件下复现的问题才是问题。我稳定的复现了问题条件是:分页组件出现向前跳转 5 页或向后跳转 5 页,点击到不会再出现向前跳转 5 页或向后跳转 5 页这样的快速跳转后,弹框会被收回。git
肯定是本身组件配置使用问题仍是组件自己 bug
我在组件的文档中并无看到有关这个问题的特别说明。接着我又在官方提供的代码运行环境中,写了一个使用的 demo(实际项目中的代码复杂度较高,可能存在被其余样式等因素影响而产生问题,使用功能单一的 demo 有助于咱们快速肯定问题范围,且官方运行环境通常使用的是其最新的组件版本),发现与我在项目中使用存在一样问题。到这里能够排除是组件配置使用,组件库版本不是最新,及项目环境影响致使的问题。github
在组件库的开源项目中查询 issue
在 issue 中搜索关键字查询是否有相关 issue,若是运气好找到相关问题,通常会有相关的讨论,就算没有具体的解决措施也能让你明白问题大概在哪里。我运气比较差,没有找到相关问题的 issue,因此只能本身提一个 issue 了。有时间有耐心等待维护人解决你提的 issue,问题分析到这里就能够结束了。npm
// el 表示指令绑定的元素,event为点击事件 const isClickOutside = event.target !== el && !el.contains(event.target) if (!isClickOutside) { return } if (middleware(event, el)) { handler(event, el) }
问题已经找出,如今问题是怎么解决,由于问题是出在组件中,可是咱们又不能直接修改组件库代码,直接修改项目依赖代码,不利于代码维护,也治标不治本。最好是能在项目代码中添加代码解决这个问题。ide
开始时走了一些弯路,想着用伪元素去覆盖,被点击的元素,试图去混淆 e.target,让 el.contains(event.target) 为true。这样确实取得了一些成效,可是在页数更多了之后依然有问题,并且我也不是很清楚这样用伪元素遮挡可让事件触发元素不是原本元素的原理是什么。更重要的一点是,写这篇文章的时候我彻底回忆不出当时我为何会想到这个方案,多是灵感吧,解决问题真的很须要灵感。函数
在使用了弯路解决办法暂时解决问题后,我开始思考更完全的解决方案。归根结底问题是出在了 v-click-outside 指令上,若是我够找到一种正确的判断方式,让被删除的元素也能够被判断为在指令注册元素中,那么问题将获得完全解决。通过不断的尝试与思考,我发现将点击事件注册在捕获阶段触发时,获得的指令绑定元素中依然有应该被删除的点击元素。到这里问题基本就明朗了,完全的解决方案也就出现了。调试
因为组件引入的 v-click-outside 指令是局部注册在下拉弹框组件上的,因此我使用了 vue 的extends 继承了组件的弹框下拉组件。在继承的组件中从新注册了指令 v-click-outside,该指令注册在 document 上的点击事件是捕获阶段触发的。code
因为是继承覆盖组件库中的组件,因此组件库升级不会带来太大的影响,同时也不用从新写一个组件,减小了工做量。到这里问题就获得了完全的解决。
解决完问题以后,我在 npm 上搜索了一些 click-outside 相关的包,发现这些包中注册在 document 上的点击事件,广泛是在冒泡阶段触发的,也就是说都存在文中我所遇到的问题。因而通过几天业余时间的努力,我开源了一个基于 vue 的 click-outside 指令开源项目catch-click-outside(不要脸求star)
在以前的工做中,我也解决了很多问题,此次之因此会记录解决过程,一个是由于这个问题我产生了一些输出,可能对别人会有些微的帮助。另外一个比较重要的缘由是,在解决问题过程当中走了一些弯路,也遇到了一些坎,可是这些问题都在不停的思考与事件中逐渐清晰,而后被解决,我以为这个过程很值得记录。文字功底有限,这个解决过程记录的平淡无奇,谨以此做为备忘。