给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这样的知名开源组件库使用它时也难免出现问题。
给开源项目做了点贡献,自己也进步了一点,感觉良好。
