Introduction
During a project for a client, I was tasked with integrating an event listener within a Vue-based Single Page Application (SPA), specifically for a component named "HomeView" that contained an iframe. The event, dubbed "triggerEvent," was initially set up during the 'onMount' lifecycle hook of HomeView. This setup led to unexpected behavior where navigating away and then returning to HomeView caused the "triggerEvent" to fire multiple times, indicating a stacking of event listeners.
Investigating the Issue
To understand the issue better, I experimented with location.replace()
instead of router.push()
for navigation. The former seemed to resolve the issue, suggesting that the method of navigation affected how event listeners behaved. Further tests with router.push()
confirmed that each navigation added a new listener without removing the previous one, thereby multiplying the triggers.
The Root Cause
This behavior is rooted in how SPA frameworks like Vue manage event listeners. In Vue, components mounted with onMount
do not automatically clean up their event listeners upon unmounting unless explicitly coded to do so. This leads to duplicated listeners if a component mounts multiple times, as is common in SPAs where router.push()
is used for navigation without full page reloads.
Solutions
Cleanup on Component Unmount
To prevent this issue, one effective strategy is to remove event listeners when the component unmounts:
import { onMounted, onUnmounted } from 'vue';
export default {
setup() {
const handleEvent = () => console.log('Event triggered');
onMounted(() => window.addEventListener('triggerEvent', handleEvent));
onUnmounted(() => window.removeEventListener('triggerEvent', handleEvent));
}
};
Conditional Event Listener Attachment
Alternatively, checking if an event listener is already in place before adding a new one can prevent duplicates:
import { onMounted, ref } from 'vue';
const eventBound = ref(false);
export default {
setup() {
const handleEvent = () => console.log('Event triggered');
onMounted(() => {
if (!eventBound.value) {
window.addEventListener('triggerEvent', handleEvent);
eventBound.value = true;
}
});
}
};
Global Event Listener in App.vue
Another approach is to place the event listener in App.vue
, which is only mounted once, thereby avoiding any reattachment issues:
import { onMounted } from 'vue';
export default {
setup() {
const handleEvent = () => console.log('Global event triggered');
onMounted(() => window.addEventListener('triggerEvent', handleEvent));
}
};
Top comments (0)