wagoon
wagoon

Reputation: 173

Vue.js 3 unmount and memory leak

For purpose of this post i created a simple example: http://wagoon.demoeshop.net/test-remove-vue.html

In this example you will find two buttons.

Example code

In my example, you will find two buttons

<button type="button" onclick="myTest.mount()">.mount()</button>
<button type="button" onclick="myTest.unmount()">.unmount()</button>

Vue.js 3 is included

<script src="https://unpkg.com/vue@next"></script>

Whole javascript code is wrapped in function testClass() for debugging reasons:

function testClass(){

   // vueApp is public just for debugging reasons
   this.vueApp = null;

   // creates DIV with id #appDiv and apends it to <body>
   function createDiv(){
      var div = document.createElement('div');
      div.id = "appDiv";
      document.body.append(div);
   }

   // creates new Vue app and mounts it to #appDiv
   this.mount = function(){
      createDiv();
      this.vueApp = Vue.createApp({"template":"Vue mounted"});
      this.vueApp.mount('#appDiv');
   }

   // unmounts Vue app
   this.unmount = function(){
      // MEMORY LEAK HERE:
      this.vueApp.unmount('#appDiv'); // this line should mark vueApp as collectable for garbage collector,it's not
      this.vueApp = null; // event this line does not help

      // SOLUTION: only removing app taget element from DOM is marking object created with Vue.createApp()
      // as collectable by garbage collector.
      // document.querySelector('#appDiv').remove();
   }

}

myTest = new testClass();

How to find memory leak in google chrome console:

For debugging reason, created app is stored to this.vueApp in testClass so we can find the object id easily. Just follow these steps

  1. Run the code
  2. click on first button (.mount Vue app). "Vue mounted" text will appear
  3. open chrome console and switch to Memory tab
  4. Take a heap snapshot
  5. click on second button (.unmount Vue app). "Vue mounted" text will disappear
  6. back on the Memory tab click on "Collect garbage" (icon with dustbin)
  7. Take second heap snapshot
  8. Switch to first taken snapshot and filter "testClass". (you will see only one result). Open it and find public property "vueApp". Next to it you will find @ID of object stored in this property (for example @567005)
  9. Switch to second snapshot and press CTRL+F (find). Search for the same @ID (for example @567005). Here is memory leak: object created with Vue.createApp is still in memory! It was NOT collected with garbage collector, because something is still pointing to this object

How to solve this memory leak

Only solution I found is removing DIV#appDiv from the DOM (code for removing this element is commented in the myTest.unmount() method). After that, calling garbage collector again will remove this object from memory.

Is there any other solution?

Why is this (big) problem

In big apps with multiple screens, creating and deleting whole app is the only way, how to save memory (script just loads code for actual page, and when user wants another page, actual page is destroyed and new page is loaded, then new Vue app is created)

You can't also solve this problem with creating dynamic components, because Vue3 removed (and i thing its big mistake) the $destroy method, so when you create new component for new screen, the old component will remain in memory forever.

Vue router will not solve this problem, because Vue router loads all pages on start and that is not acceptable in big apps, because the network bandwidth will be huge (megabytes of code loaded for just one app is just wrong)

Upvotes: 5

Views: 5595

Answers (1)

wagoon
wagoon

Reputation: 173

Fixed in VUE 3.0.6

VUE js version 3.0.6 fixed this problem

Upvotes: 2

Related Questions