Vue.js 3 卸载和内存泄漏

Vue.js 3 unmount and memory leak

为此 post 我创建了一个简单的示例: http://wagoon.demoeshop.net/test-remove-vue.html

在此示例中,您将找到两个按钮。

示例代码

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

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

Vue.js包含3个

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

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

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();

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

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

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

如何解决此内存泄漏

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

还有其他解决办法吗?

为什么这个(大)问题

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

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

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

已在 VUE 3.0.6 中修复

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