Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$emit causing memory leak - Vue.js

Tags:

vue.js

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

Memory snapshots

This is best visible by making memory snapshots after certain actions. When the page loads, we have a single Vue instance. On page load

After clicking toggle, a VueComponent is created as expected. After clicking "toggle"

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. After clicking "Close me"

like image 685
T. Short Avatar asked Aug 20 '19 17:08

T. Short


People also ask

How will you solve common causes of memory leaks in VUE JS applications?

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.

Why we use $emit in VUE JS?

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.

What causes memory leak in JavaScript?

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.


1 Answers

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.

like image 138
skirtle Avatar answered Sep 28 '22 04:09

skirtle