在前端编码中,经常会通过addEventListener来添加全局的事件监听,但是,这种全局的事件监听方法若使用不当,很容易造成严重的性能问题,下面我就来说一下如何正确的添加和销毁全局的事件监听。
添加
1、element.addEventListener的使用方法
这里我就不多啰嗦了,网上已经讲的很明白了,下面附上链接。
https://www.runoob.com/jsref/met-element-addeventlistener.html
2、window.addEventListener和document.addEventListener
我们知道,document是window下的一个对象,一般情况下,这两个都可以添加全局的事件监听,区别就是,执行的顺序会不太一样。
如果addEventListener没有第三个参数或第三个参数为false的话,也就是说在冒泡阶段执行,document.addEventListener的绑定事件会先于window.addEventListener的绑定事件执行。
若addEventListener的第三个参数为true的话,也就是说在捕获阶段执行,window.addEventListener的绑定事件会先于document.addEventListener的绑定事件执行。
这个应该比较好理解,就是捕获和冒泡的事件执行顺序的区别。
3、不使用匿名函数
下面我列举两种添加事件监听的写法:
function handleClick () {
alert('hello!')
}
// 第一种
document.addEventListener('click', handleClick)
// 第二种
document.addEventListener('click', () => {
alert('hello!')
})
这两种写法看起来没有什么大的区别,也都能实现同样的效果,但实际上第二种写法是错误的,一旦把这种匿名函数添加到全局的事件监听当中,会导致无法销毁这个监听事件。
原因也很简单,函数名会作为销毁监听事件时的索引,但是匿名函数没有函数名,所以我们也就无法获得其索引,也就无法销毁此监听事件。
销毁
1、参数与创建时要一致
还是先举两个例子:
// 正确
document.addEventListener('click', handleClick, true)
document.removeEventListener('click', handleClick, true)
document.addEventListener('resize', handleResize)
document.removeEventListener('resize', handleResize)
// 错误
document.addEventListener('click', handleClick, true)
document.removeEventListener('click', handleClick)
创建和销毁时传入的三个参数要完全一致,否则无法无法销毁。
2、注意第二个参数的有效性
这是什么意思呢?
在项目中,一个常见的场景就是:在页面进入后创建监听事件,并在页面离开后把这个监听销毁掉。
那么当页面销毁时,伴随着页面的一些方法很可能也跟着销毁或者改变了,当这时我们再去销毁事件监听时,实际上方法已经取不到了,但我们却浑然不知,举个例子:
let handleClick = () => {
alert('Hello!')
}
document.addEventListener('click', handleClick)
handleClick = () => {
alert('World!')
}
document.removeEventListener('click', handleClick)
例子比较简陋,就是希望大家能明白,添加和销毁时的方法一定要是同一个方法,确保没有被销毁或改变。
总结
其实写这篇文章,添加监听器的使用我是不太担心的,因为使用方法非常简单。但好多人都没有及时销毁监听器的意识或不会正确的销毁监听器,这样做导致的直接后果就是内存泄露。
说到内存泄露,有些人可能会质疑,一个小小的监听器能占用多少内存?
譬如说一个Vue的组件,全局的监听器引用了Vue实例里的方法,那么当这个Vue组件不在使用的时候,因为Vue实例内部的方法被引用,所以就导致Vue实例占用的内存无法被释放,而这个组件内部的数据可能很多,还有可能引用了其他的组件等等,最终就回导致严重的内存泄露,产生严重的性能问题。
我不是在危言耸听,我参与的半数以上的项目都会遇到因某些开发者粗心大意或意识不够强,没有正确的销毁监听器而引起的内存泄露,有些网页在短时间操作后,其内存占用甚至能从十几MB飙升至上百MB,有上百个全局的监听事件。
我希望所有的前端开发者能有这样一个意识,在项目开发的过程中,不时的关注一下页面的性能问题,谷歌浏览器提供了两个很好的工具,通过控制台的Memory和Performance,我们能轻松的观察、分析页面的内存使用情况和性能。后面我会简单介绍一下这两个工具的使用。