一个开源组件 bug 引发的分析
这是一个悲伤的故事。某日清晨,距离版本转测还剩一天,切图仔的我正按照计划有条不紊的画页面。当我点击一个下拉弹框组件中分页组件页数过多而出现的向后 5 页省略号时,悲剧开始了,弹框被收回了。情景再现
问题
问题的表象很简单,使用的是组件库的下拉弹窗组件,在组件中使用到了分页组件,当点击分页组件的向后 5 页快速跳转时,弹窗被收回了。我们的预期是能够继续操作的,只有点击弹框外部时,弹窗才会被收回。
分析
发现这个问题我做了如下分析:
确定这是一个问题
再次重复操作问题,确定问题出现的条件,能够在特定条件下复现的问题才是问题。我稳定的复现了问题条件是:分页组件出现向前跳转 5 页或向后跳转 5 页,点击到不会再出现向前跳转 5 页或向后跳转 5 页这样的快速跳转后,弹框会被收回。确定是自己组件配置使用问题还是组件本身 bug
我在组件的文档中并没有看到有关这个问题的特别说明。接着我又在官方提供的代码运行环境中,写了一个使用的 demo(实际项目中的代码复杂度较高,可能存在被其他样式等因素影响而产生问题,使用功能单一的 demo 有助于我们快速确定问题范围,且官方运行环境一般使用的是其最新的组件版本),发现与我在项目中使用存在同样问题。到这里可以排除是组件配置使用,组件库版本不是最新,及项目环境影响导致的问题。在组件库的开源项目中查询 issue
在 issue 中搜索关键字查询是否有相关 issue,如果运气好找到相关问题,一般会有相关的讨论,就算没有具体的解决措施也能让你明白问题大概在哪里。我运气比较差,没有找到相关问题的 issue,所以只能自己提一个 issue 了。有时间有耐心等待维护人解决你提的 issue,问题分析到这里就可以结束了。查看组件源码
显然我的时间很紧急,问题必须尽快解决,且我也想确定一下问题具体在哪里,看自己能否解决。很快我找到的相关组件的源码,经过定位发现,弹框下拉组件中使用了 v-click-outside 指令,用来触发点击指令之外的元素收起弹框。
进一步深入源码,发现 v-click-outside 组件的原理,是在 document 组件上注册了一个 click 监听事件,当有点击事件发生,监听函数会判断,点击事件发生的元素,是否包含在使用指令的元素中。如果是监听函数返回,否则触发指令绑定的事件(在弹框组件中就是隐藏弹框)。代码片段如下
// el 表示指令绑定的元素,event为点击事件
const isClickOutside = event.target !== el && !el.contains(event.target)
if (!isClickOutside) {
return
}
if (middleware(event, el)) {
handler(event, el)
}
- 断点调试
经过上面的步骤过后,我还没有发现问题的所在,所以只能打断点进行代码调试了。经过调试发现问题出在 el.contains(event.target) 这一段,分页组件中的向前 5 页元素,点击触发后就被 v-if 指令从页面中移除了。所以运行到这一句时,el 中是不包含向前 5 页元素的。也就是说这里向前 5 页元素被错误的判断为不在 el 元素中,指令绑定的弹框收起函数被触发,弹框被收回了。
解决问题
问题已经找出,现在问题是怎么解决,因为问题是出在组件中,但是我们又不能直接修改组件库代码,直接修改项目依赖代码,不利于代码维护,也治标不治本。最好是能在项目代码中添加代码解决这个问题。
开始时走了一些弯路,想着用伪元素去覆盖,被点击的元素,试图去混淆 e.target,让 el.contains(event.target) 为true。这样确实取得了一些成效,但是在页数更多了以后依然有问题,而且我也不是很清楚这样用伪元素遮挡可以让事件触发元素不是本来元素的原理是什么。更重要的一点是,写这篇文章的时候我完全回忆不出当时我为什么会想到这个方案,可能是灵感吧,解决问题真的很需要灵感。
在使用了弯路解决办法暂时解决问题后,我开始思考更彻底的解决方案。归根结底问题是出在了 v-click-outside 指令上,如果我够找到一种正确的判断方式,让被删除的元素也可以被判断为在指令注册元素中,那么问题将得到彻底解决。经过不断的尝试与思考,我发现将点击事件注册在捕获阶段触发时,得到的指令绑定元素中依然有应该被删除的点击元素。到这里问题基本就明朗了,彻底的解决方案也就出现了。
由于组件引入的 v-click-outside 指令是局部注册在下拉弹框组件上的,所以我使用了 vue 的extends 继承了组件的弹框下拉组件。在继承的组件中重新注册了指令 v-click-outside,该指令注册在 document 上的点击事件是捕获阶段触发的。
由于是继承覆盖组件库中的组件,所以组件库升级不会带来太大的影响,同时也不用重新写一个组件,减少了工作量。到这里问题就得到了彻底的解决。
输出 v-click-out 包
解决完问题之后,我在 npm 上搜索了一些 click-outside 相关的包,发现这些包中注册在 document 上的点击事件,普遍是在冒泡阶段触发的,也就是说都存在文中我所遇到的问题。于是经过几天业余时间的努力,我开源了一个基于 vue 的 click-outside 指令开源项目catch-click-outside(不要脸求star)
一点思考
在之前的工作中,我也解决了不少问题,这次之所以会记录解决过程,一个是因为这个问题我产生了一些输出,可能对别人会有些微的帮助。另一个比较重要的原因是,在解决问题过程中走了一些弯路,也遇到了一些坎,但是这些问题都在不停的思考与事件中逐渐清晰,然后被解决,我觉得这个过程很值得记录。文字功底有限,这个解决过程记录的平淡无奇,谨以此作为备忘。
转载请注明出处!
一个开源组件 bug 引发的分析的更多相关文章
- android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据
补充:关于PHP服务端可能出现的问题: 如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!! 在android应用中访 ...
- Android自定义控件 开源组件SlidingMenu的项目集成
在实际项目开发中,定制一个菜单,能让用户得到更好的用户体验,诚然菜单的样式各种各样,但是有一种菜单——滑动菜单,是被众多应用广泛使用的.关于这种滑动菜单的实现,我在前面的博文中也介绍了如何自定义去实现 ...
- .net 开源组件推荐 之 StackExchange
已经两年没更新过博客了!!! StackExchange,地址:https://github.com/StackExchange,开源的这些项目都是在StackOverflow线上使用的. 说起Sta ...
- z-index失效原因分析——由一个bug引发的对层叠上下文和z-index属性的深度思考
新年刚开工就被一个bug虐得整个人都不好了,特地记录下. (一)bug描述 在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段提示的特效,所以做了一个提示层,但 ...
- FluentConsole是一个托管在github的C#开源组件
FluentConsole是一个托管在github的C#开源组件 阅读目录 1.控制台能有啥滑头? 2.FluentConsole基本介绍 3.使用介绍 4.资源 从该系列的第一篇文章 .NET平台开 ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- 开源组件ExcelReport 1.5.2 使用手册
ExcelReport是一款基于NPOI开发的报表引擎组件.它基于关注点分离的理念,将数据与样式.格式分离.让模板承载样式.格式等NPOI不怎么擅长且实现繁琐的信息,结合NPOI对数据的处理的优点将E ...
- .net 开源组件
文章转自:http://www.cnblogs.com/asxinyu/p/dotnet_opensource_project_3.html 在前2篇文章这些.NET开源项目你知道吗?让.NET开 ...
- 一个MySQL-JDBC驱动bug引起的血案……
问题背景 公司是做电商系统的,整个系统搭建在华为云上.系统设计的时候,考虑到后续的用户和订单数量比较大,需要使用一些大数据库的组件.关系型数据库这块,考虑到后续数据量的快速增长,不是直接写入MySQL ...
随机推荐
- linux分析利刃之sar命令详解
一.sar的概述 在我使用的众多linux分析工具中,sar是一个非常全面的一个分析工具,可以比较瑞士军刀,对文件的读写,系统调用的使用情况,磁盘IO,CPU相关使用情况,内存使用情况,进程活动等都可 ...
- windows下python和pycharm安装及其使用
1.python安装及环境变量配置 1.1 python安装 1.1.1 python下载 官网下载:https://www.python.org/ Downloads-Windows(Mac os ...
- 学习WEBAPI(DOM)第二天
目录 第二天学习目标: 一.阻止超链接的默认跳转行为 二.鼠标进入事件和鼠标离开事件 三.根据name属性值获取元素==>表单标签,返回的是伪数组 四.根据类样式的名字来获取元素,返回的是伪数组 ...
- Java 学习笔记之 线程Priority
线程Priority: 线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务. 设置线程优先级有助于帮助“线程规划器”确定在下一次选择哪个线程来优 ...
- MongoDB 学习笔记之 replica set搭建
Replica set搭建: 修改mongodb.conf文件,指明replSet 登入客户端,指定副本集成员,进行初始化, 如果priority需要调整,使用reconfig()方法.Seconda ...
- mysql配置默认字符集为UTF8mb4
[client] default-character-set=utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = ut ...
- C# 获取顶级(一级)域名方法
/// <summary> /// 获取域名的顶级域名 /// </summary> /// <param name="domain">< ...
- phpstorm中xdebug配置和断点调试
今天赶紧把这个搞了 运行环境: phpStorm 2018 PHP 5.45 nts VC9 Xdebug 2.4.1 0x01 PHP安装xdebug扩展 自己之前装一直没装起 原来是版本问题 下 ...
- docker配置阿里云镜像
今天docker pull镜像的时候太慢了 所以这里配置下阿里云镜像 打开阿里云控制台,没有的可以用淘宝账号或者支付宝账号直接登录 打开容器镜像服务,镜像加速器,复制加速器地址 修改配置文件 $: ...
- Kali桥接模式DHCP自动获取IP失败(VMware)
Kali桥接模式DHCP自动获取IP失败笔者用的是VMware运行Kali Linux,突然发现桥接模式无法上网,只能使用NAT模式.身为有一点点强迫症的人来说,这就很不爽了.于是马上切换为桥接模式, ...