Vue.js 3 卸载和内存泄漏

wag*_*oon 5 memory-leaks vue.js vuejs3

为了这篇文章的目的,我创建了一个简单的示例: http://wagoon.demoeshop.net/test-remove-vue.html

在此示例中,您会发现两个按钮。

  • 第一个按钮创建DIV元素,然后创建一个 Vue 应用程序并将其安装到该应用程序div
  • 第二个按钮将取消安装应用程序

示例代码

在我的示例中,您会发现两个按钮

<button type="button" onclick="myTest.mount()">.mount()</button>
<button type="button" onclick="myTest.unmount()">.unmount()</button>
Run Code Online (Sandbox Code Playgroud)

包含 Vue.js 3

<script src="https://unpkg.com/vue@next"></script>
Run Code Online (Sandbox Code Playgroud)

testClass()出于调试原因,整个 javascript 代码都包装在函数中:

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();
Run Code Online (Sandbox Code Playgroud)

如何在 google chrome 控制台中查找内存泄漏:

出于调试原因,创建的应用程序存储在 testClass 中的 this.vueApp 中,以便我们可以轻松找到对象 id。只需按照以下步骤操作即可

  1. 运行代码
  2. 单击第一个按钮(.mount Vue 应用程序)。将出现“Vue 已安装”文本
  3. 打开 chrome 控制台并切换到内存选项卡
  4. 拍摄堆快照
  5. 单击第二个按钮(.unmount Vue 应用程序)。“Vue Mounted”文字将会消失
  6. 返回“内存”选项卡,单击“收集垃圾”(带垃圾箱的图标)
  7. 拍摄第二个堆快照
  8. 切换到第一个拍摄的快照并过滤“testClass”。(您只会看到一个结果)。打开它并找到公共属性“vueApp”。在它旁边,您将找到存储在该属性中的对象的@ID(例如@567005)
  9. 切换到第二个快照并按 CTRL+F(查找)。搜索相同的@ID(例如@567005)。这是内存泄漏:使用 Vue.createApp 创建的对象仍在内存中!它没有被垃圾收集器收集,因为有些东西仍然指向这个对象

如何解决这个内存泄漏问题

我找到的唯一解决方案是DIV#appDiv从 DOM 中删除(删除此元素的代码已在myTest.unmount()方法中注释)。之后,再次调用垃圾收集器将从内存中删除该对象。

还有其他解决办法吗?

为什么这是一个(大)问题

在具有多个屏幕的大型应用程序中,创建和删除整个应用程序是唯一的方法,如何节省内存(脚本只是加载实际页面的代码,当用户想要另一个页面时,实际页面被销毁并加载新页面,然后新的Vue应用程序已创建)

你也不能通过创建动态组件来解决这个问题,因为 Vue3 删除了(我认为这是一个很大的错误) $destroy 方法,所以当你为新屏幕创建新组件时,旧组件将永远保留在内存中。

Vue 路由器不会解决这个问题,因为 Vue 路由器在启动时加载所有页面,这在大型应用程序中是不可接受的,因为网络带宽将很大(仅为一个应用程序加载兆字节的代码是错误的)

wag*_*oon 2

VUE 3.0.6 中已修复

VUE js 3.0.6版本修复了这个问题