I have two components. A parent and a child. The parent loads the child based on v-if
. Then the child sends an event through $emit
to tell the parent to remove the child.
So simply put.
PARENT:
data() {
return {
show: false
}
},
// template:
<div>
<div @click="show = !show">Toggle</div>
<child-component
v-if="show"
@close="show = false"
/>
</div>
CHILD:
template:
<div>
Some Content
<div @click="$emit('close')">Close</div>
</div>
The problem is that when $emit('close')
is fired, the child is removed from the DOM but the component is not cleared from memory.
If the parent removes the child using the toggle
button, it does clear the child from memory.
I have also tried using Vuex store
and $root.data
but this also causes a memory leak.
In other words, it seems that if the child signals to the parent that it should be removed, it is kept in memory. However, if the parent removes the child directly (without any signal from the child) it's removed from memory...
Any ideas why this happens and what I should do to prevent this memory leak? It's necessary that the child signal it's removal.
[EDIT] - DEMO IN CODE PEN. https://codepen.io/tomshort5/pen/BaBLXvb
This is best visible by making memory snapshots after certain actions. When the page loads, we have a single Vue instance.
After clicking toggle, a VueComponent is created as expected.
After triggering the child to be removed through an event, the VueComponent is not removed from memory. When comparing it to clicking "Toggle" again, it does show the component getting removed from memory.
Resolving the Memory Leak In the above example, we can use our hide() method to do some clean up and solve the memory leak prior to removing the select from the DOM. To accomplish this, we will keep a property in our Vue instance's data object and we will use the Choices API's destroy() method to perform the clean up.
Vue $emit is a function that lets us emit, or send, custom events from a child component to its parent. In a standard Vue flow, it is the best way to trigger certain events.
The main cause of memory leaks in an application is due to unwanted references. The garbage collector finds the memory that is no longer in use by the program and releases it back to the operating system for further allocation.
I have a partial explanation.
It seems that something is holding onto a reference to the last DOM node that was clicked. So if you click on Close
it will grab hold of a reference to that text node. The text node's parent is the <div>
and the <div>
has a click listener pointing back to the Vue component. The component itself has been destroyed, it just can't be GCed.
If instead you click on Toggle
it retains a reference to the Toggle
text node. As that node is in a different section of the DOM tree it doesn't retain any reference to the element with the click listener. The Vue component is successfully GCed.
I haven't been able to establish precisely what retains this reference to the last clicked node. The heap snapshot isn't particularly helpful. It just shows InternalNode
connecting window
to the relevant text node.
I did put together a page to explore this without Vue. My experiments suggested that retaining a reference to the last clicked node is something that occurs even when Vue is not on the page.
The memory leak here is only very temporary. Clicking one more time, anywhere on the page, seems to be enough to fix it. Consistent with my theory, that updates the 'last clicked node' reference, allowing the detached DOM nodes to be GCed and the Vue component with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With