给PrimeVue组件库提了个PR
背景 项目用的组件库是基于Element Plus开发的(后面简称elm),但在使用过程中发现elm的tooltip组件有内存泄漏问题: 进一步排查,发现是Vue-3.3版本下,在内置的transition组件中使用v-if会有问题 定位到这个因素,是因为在element-plus的playground中,使用vue的3.3.x版本存在内存泄漏,而3.5.13版本不存在 具体排查过程就不放了,还有一篇超长内存泄漏blog正在填坑中 因早就嫌弃elm的tooltip组件使用方式不够优雅,遂把目光转向另一个大名鼎鼎的开源Vue3组件库——PrimeVue。 PrimeVue以指令的方式提供了tooltip,比使用组件优雅很多(其实博主已经把elm的tooltip二次封装为指令形式使用了) 正文 问题定位 通过对PrimeVue进行了与elm相同的内存泄漏测试(反复触发tooltip显隐),发现这货的tooltip居然也有内存泄漏问题(如下图),再次印证了草台班子理论。 一番排查后,最终定位到导致泄露的代码: 可以看出只要不触发resize事件,onWindowResize方法的闭包作用域中会一直持有对el的引用 查一查有没有相关的PR: 发现这个问题还没人解决,目测修复也比较简单,故准备给组件库提个PR 梳理逻辑 先来看看PrimeVue的tooltip都有啥 可以看到里面有跟Vue指令定义差不多的生命周期方法和一部分自己的内部方法methods,图中框起来的部分就是产生内存泄漏的主要代码 其中的BaseTooltip是PrimeVue在Vue的ObjectDirective基础上做了二次封装及自己的一些扩展的方法 顾名思义,show方法用于元素的显示,hide方法用于元素隐藏,tooltipRemoval则用于移除tooltip元素,展开看一下内部代码 tooltipActions代码: 到这里我们可以知道: 在触发指令绑定元素的mouseEnter和focus事件时,会调用show方法显示tooltip show方法会调用tooltipActions,而tooltipActions中的会创建window上的resize事件监听器,且不触发就不会被释放,进而导致el一直被该监听器方法创建的闭包作用域引用 看的过程中发现两行不对劲代码,准备顺便一起改了: 修复思路 首先,我们需要能够从外面引用到监听器,以便在其他地方销毁,先将其从tooltipActions里提出来 接下来,需要解决onWindowResize中el参数和this指向的问题,显然应该用bind完成(methods中有bindEvents方法),在bindEvents中添加: 再在tooltipRemoval和unbindEvents里加上销毁监听器的代码 测试效果 构建组件库到本地,link到本地项目测试,反复触发显隐,查看内存快照对比 问题解决,效果符合预期 单元测试,启动! 从项目根目录的package.json中可以看到组件库是有单元测试的 执行一下单元测试,确保没有产生新bug(内存泄漏的单元测试还没研究怎么写,以后整明白了再帮他们补上) 提交PR 提交代码,填写信息,提交PR表单一气呵成 并没有,好久不用Github提代码发现不能用密码登录了,又去建了个token才提上去 贴个PR链接,方便以后查(xin)看(shang) 总结 开发过程中用到window.addEventListener方法时需要谨慎,毕竟像PrimeVue这样的知名开源组件库使用它时也难免出现问题。 给开源项目做了点贡献,自己也进步了一点,感觉良好。