在 Vue 中移除原生 JavaScript 事件監(jiān)聽器(即通過 addEventListener 綁定的事件),核心原則與原生 JS 一致:在組件合適的生命周期鉤子中,使用 removeEventListener 移除,且確保事件類型、回調(diào)函數(shù)引用、捕獲階段參數(shù)與綁定時代完全匹配。以下是針對 Vue 3 和 Vue 2 的具體實現(xiàn)方法及避坑指南:
移除原生事件監(jiān)聽器的關(guān)鍵是 “參數(shù)三匹配”:
- 事件類型(如
click、scroll)一致;
- 回調(diào)函數(shù)引用一致(不能用匿名函數(shù));
- 捕獲階段參數(shù)(
useCapture,默認 false)一致。
錯誤示例(匿名函數(shù)無法移除):
boxRef.value.addEventListener('click', () => console.log('點擊'));
boxRef.value.removeEventListener('click', () => console.log('點擊'));
正確示例(具名函數(shù)確保引用一致):
function handleClick() {
console.log('點擊');
}
boxRef.value.addEventListener('click', handleClick);
boxRef.value.removeEventListener('click', handleClick);
Vue 3 中需通過 ref 獲取 DOM 元素引用,在組件掛載時綁定事件,在卸載前(onUnmounted)移除,確保組件銷毀時事件被清理。
<template>
<div ref="box" class="box">點擊或移動鼠標</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
// 1. 獲取 DOM 引用
const box = ref(null);
// 2. 定義具名回調(diào)函數(shù)(確保引用唯一)
function handleClick() {
console.log('盒子被點擊');
}
function handleMouseMove(e) {
console.log('鼠標位置:', e.clientX, e.clientY);
}
// 3. 組件掛載時綁定事件
onMounted(() => {
if (box.value) {
box.value.addEventListener('click', handleClick); // 綁定 click
box.value.addEventListener('mousemove', handleMouseMove); // 綁定 mousemove
}
});
// 4. 組件卸載時移除所有事件(關(guān)鍵步驟)
onUnmounted(() => {
if (box.value) {
box.value.removeEventListener('click', handleClick);
box.value.removeEventListener('mousemove', handleMouseMove);
}
});
</script>
<style>
.box { width: 200px; height: 200px; background: #eee; }
</style>
若綁定事件時使用了捕獲階段(addEventListener 第三個參數(shù)為 true),移除時必須傳入相同參數(shù):
onMounted(() => {
box.value.addEventListener('click', handleClick, true);
});
onUnmounted(() => {
box.value.removeEventListener('click', handleClick, true);
});
若組件內(nèi)綁定了多個原生事件,可通過數(shù)組批量管理,遍歷移除:
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
const box = ref(null);
// 存儲所有原生事件配置(類型 + 回調(diào) + 捕獲階段)
const domEvents = [
{ type: 'click', handler: handleClick, useCapture: false },
{ type: 'mousemove', handler: handleMouseMove, useCapture: false },
{ type: 'mouseleave', handler: handleMouseLeave, useCapture: true }
];
function handleClick() { /* ... */ }
function handleMouseMove() { /* ... */ }
function handleMouseLeave() { /* ... */ }
onMounted(() => {
if (box.value) {
domEvents.forEach(({ type, handler, useCapture }) => {
box.value.addEventListener(type, handler, useCapture);
});
}
});
onUnmounted(() => {
if (box.value) {
domEvents.forEach(({ type, handler, useCapture }) => {
box.value.removeEventListener(type, handler, useCapture);
});
}
});
</script>
Vue 2 中通過 $refs 獲取 DOM 元素,在 mounted 中綁定事件,在 beforeDestroy 中移除,回調(diào)函數(shù)定義在 methods 中確保引用穩(wěn)定。
<template>
<div ref="box" class="box">點擊我</div>
</template>
<script>
export default {
methods: {
// 回調(diào)函數(shù)定義在 methods 中,引用唯一
handleClick() {
console.log('盒子被點擊');
}
},
mounted() {
// 組件掛載后綁定事件
this.$refs.box.addEventListener('click', this.handleClick);
},
beforeDestroy() {
// 組件銷毀前移除事件(關(guān)鍵)
this.$refs.box.removeEventListener('click', this.handleClick);
}
};
</script>
若 DOM 元素通過 v-if 控制顯示 / 隱藏,需在元素銷毀前(如 beforeDestroy)判斷元素是否存在,避免報錯:
beforeDestroy() {
if (this.$refs.box) {
this.$refs.box.removeEventListener('click', this.handleClick);
}
}
- 問題:綁定匿名函數(shù)或動態(tài)創(chuàng)建的函數(shù),導致
removeEventListener 找不到相同引用。
- 解決方案:始終使用具名函數(shù)(如
function handleClick())或在組件實例上保存函數(shù)引用(如 Vue 2 的 methods、Vue 3 的 setup 內(nèi)定義)。
- 問題:綁定事件時用了
useCapture: true,移除時未傳入,導致移除失敗。
- 解決方案:移除時嚴格保持
useCapture 參數(shù)與綁定一致。
- 問題:組件內(nèi) DOM 通過
v-if 銷毀,或組件卸載時 DOM 已被移除,此時調(diào)用 removeEventListener 會報錯。
- 解決方案:移除前先判斷 DOM 元素是否存在(如
if (box.value))。
- 問題:通過 Vue 的
@click 綁定的事件,無需手動移除(Vue 會自動清理),但如果同時用 addEventListener 綁定了相同事件,需手動移除。
<template>
<!-- Vue 指令綁定的事件:自動移除 -->
<button @click="vueClick">Vue 指令點擊</button>
<!-- 原生 addEventListener 綁定的事件:需手動移除 -->
<button ref="nativeBtn">原生事件點擊</button>
</template>
- 綁定階段:用
ref(Vue 3)/$refs(Vue 2)獲取 DOM 引用,使用具名函數(shù)通過 addEventListener 綁定事件,記錄事件類型、回調(diào)、捕獲階段參數(shù)。
- 移除階段:在組件卸載鉤子(
onUnmounted Vue 3 /beforeDestroy Vue 2)中,通過 removeEventListener 移除事件,確保參數(shù)與綁定完全一致。
- 兜底處理:移除前判斷 DOM 是否存在,避免報錯;批量事件通過數(shù)組管理,遍歷移除提高效率。
遵循以上步驟,可徹底避免原生事件監(jiān)聽器殘留導致的內(nèi)存泄漏問題。 |