vue中为什么需要手动移除事件监听器,DOM 事件监听器

文章讨论了Vue2和Vue3中组件销毁时如何正确处理事件监听器,包括手动移除DOM事件和eventBus订阅,避免内存泄漏。特别强调了在Vue3中,尽管通常组件销毁会自动停止监听器,但在异步场景下仍需手动关闭,以防内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue2和vue3中组件销毁都会移除事件监听器:

  • 组件销毁自动移除的监听器指的是同步创建的各种watcher
  • 对于异步创建的watcher,需要手动移除
  • 对于dom事件监听器,比如使用addEventListener创建的监听器,需要手动移除
  • eventBus方式总线订阅事件,需要手动移除

不移除可能造成内存泄漏!

vue2

从vue2的生命周期知道,destroyed钩子,实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

如下代码,需要手动停止interval

<template>
  <button @click="handleClick">start</button>
</template>
<script>
export default {
  data() {
    return {
      interval: null,
    };
  },
  beforeDestroy(){
    this.handleStop()
  },
  methods: {
    handleClick() {
      this.interval = setInterval(() => {
        //some actions
        console.log("interval");
      }, 2000);
    },
    handleStop() {
      clearInterval(this.interval);
      this.interval = null;
    },
  },
};
</script>

vue3

一般情况下,一个组件被销毁或者卸载后,监听器也会被停止,而不需要我们手动去关闭监听器。但是总是有一些特殊情况,即使组件卸载了,但是监听器依然存在,这个时候其实式需要手动关闭它的,否则容易造成内存泄漏。

下面这中写法,就需要手动停止监听器:

<script setup>
import { watchEffect } from 'vue'
// 自动停止
watchEffect(() => {})
// 不会自动停止
setTimeout(() => {
  watchEffect(() => {})
}, 200)
</script>

上段代码中采用异步的方式创建了一个监听器,这个时候监听器没有与当前组件绑定,所以即使组件销毁了,监听器依然存在,当组件销毁时,监听器会继续引用该组件,防止其被垃圾回收。其实就是闭包。

用如下方法关闭:

const unwatch = watchEffect(() => {})
// 关闭监听器
unwatch()

官网解释watchEffect

### Vue3 Vite 项目中解决 JS 事件监听器数量持续增加的方法 在 Vue3 和 Vite 构建的应用程序中,随着应用的增长,可能会遇到 JavaScript 事件监听器断累积的问题。这仅会消耗更多的内存资源,还可能导致性能下降和其他潜在问题。 #### 使用组合式 API 进行生命周期管理 Vue3 提供了新的组合式 API (Composition API),使得开发者能够更灵活地处理组件的生命周期钩子函数。对于防止事件监听器泄漏而言,在 `setup` 函数内注册事件监听器,并通过 `onBeforeUnmount` 或者 `onUnmounted` 来清除这些监听器是非常重要的[^2]。 ```javascript import { onMounted, onUnmounted } from 'vue'; export default { setup() { let timerId; const handleScroll = () => console.log('scrolling...'); onMounted(() => { window.addEventListener('scroll', handleScroll); // 假设我们还需要设置一个定时器作为例子 timerId = setInterval(() => {}, 1000); }); onUnmounted(() => { window.removeEventListener('scroll', handleScroll); clearInterval(timerId); }); return {}; } } ``` 上述代码展示了如何利用 Composition API 中的 `onMounted` 和 `onUnmounted` 生命周期钩子来添加和移除全局范围内的滚动事件监听器以及清理定时器实例。这样做可以有效避免当组件被销毁时遗留下来的未解除绑定的事件处理器造成内存泄露的风险[^4]。 #### 将事件监听逻辑封装成可重用 Hook 为了提高代码复用性和维护性,建议创建自定义 hooks 来集中管理和控制特定类型的事件监听行为。下面是一个简单的 hook 实现: ```typescript // useEventListener.ts import { ref, onMounted, onUnmounted } from "vue"; function useEventListener(target: EventTarget | null, type: string, listener: EventListenerOrEventListenerObject): void { if (!target) return; onMounted(() => target?.addEventListener(type, listener)); onUnmounted(() => target?.removeEventListener(type, listener)); } export default useEventListener; ``` 此方法允许我们在多个地方轻松地挂载相同的事件监听而无需重复编写相同的功能代码片段。只需导入该 hook 并传入相应的参数即可完成操作: ```javascript import useEventListener from './useEventListener' export default { setup() { const handleClickOutside = event => { if (!(event.target instanceof HTMLElement)) return; console.log(`${event.type}: Click outside detected`); }; useEventListener(document.body, 'click', handleClickOutside); return {}; }, }; ``` 这种方法有助于保持应用程序的良好结构化设计模式的同时也解决了因频繁增删 DOM 节点而导致的事件监听器堆积问题[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值