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