一个开源组件 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 ...
随机推荐
- Python3行代码之——截图工具
最近工作需要个定时截图的小工具,用Python实现比较急直接上代码 from PIL import ImageGrab im = ImageGrab.grab() im.save(addr,'jpeg ...
- MongoDB 学习笔记之 GridFS
GridFS: GridFS 是 MongoDB 的一个用来存储/获取大型数据(图像.音频.视频等类型的文件)的规范.它相当于一个存储文件的文件系统,但它的数据存储在 MongoDB 的集合中.Gri ...
- Java 爬虫遇上数据异步加载,试试这两种办法!
这是 Java 爬虫系列博文的第三篇,在上一篇 Java 爬虫遇到需要登录的网站,该怎么办? 中,我们简单的讲解了爬虫时遇到登录问题的解决办法,在这篇文章中我们一起来聊一聊爬虫时遇到数据异步加载的问题 ...
- 你也可以写聊天程序 - C# Socket学习1
简述 我们做软件工作的虽然每天都离不开网络,可网络协议细节却不是每个人都会接触和深入了解.我今天就来和大家一起学习下Socket,并写一个简单的聊天程序. 一些基础类 首先我们每天打开浏览器访问网页信 ...
- 如何在Java中创建数组列表
为了在Java中存储动态大小的元素,我们使用了ArrayList.每当添加新元素时,它会自动增加它们的大小.ArrayList实现Java的List接口和Java的Collection的一部分. 由于 ...
- Python爬虫工程师必学——App数据抓取实战 ✌✌
Python爬虫工程师必学——App数据抓取实战 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 爬虫分为几大方向,WEB网页数据抓取.APP数据抓取.软件系统 ...
- django-drf框架中排序和查询组件
0910自我总结 django-drf框架中排序和查询组件 一查询相关 1.模糊查询 1.导入模块组件 from rest_framework.filters import SearchFilter ...
- Python_函数传参
关于函数中传递参数的相关知识 其中 万能参数 第一次听说 但感觉用处不大 后面用到再详细整理
- Ubuntu系统下制作U盘启动盘
在终端输入 sudo fdisk -l 查看U盘的路径,如/dev/sdb或/dev/sdc. 然后下载ISO文件,如文件路径为/home/raina/xxx.iso 在终端输入 sudo dd if ...
- PMP涉及的几个工作系统
PMP涉及的几个工作系统 工作系统作为事业环境因素,提高或限制项目管理的灵活性,并可能对项目结果产生积极或消极影响,包括项目管理系统.项目管理信息系统PMIS.配置管理系统.变更控制系统.合同变更 ...