【问题标题】:Vue.js 3 unmount and memory leakVue.js 3 卸载和内存泄漏
【发布时间】:2021-04-05 02:33:08
【问题描述】:

为了这篇文章的目的,我创建了一个简单的例子: 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>

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

如何解决这个内存泄漏

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

还有其他解决方案吗?

为什么这是(大)问题

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

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

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

【问题讨论】:

  • "Vue 路由器在启动时加载所有页面" - router.vuejs.org/guide/advanced/lazy-loading.html
  • 路由器正在使用 import() 并且可以以“延迟加载”方式完成(按需加载)。但这不是问题。问题是:如果您使用路由器,您不能从内存中删除(或“卸载”)importet 模块。但是这篇文章不是关于路由器的,是关于 unmount() 方法的
  • 值得在 vue3 github 中创建问题然后:github.com/vuejs/vue-next/issues
  • 谢谢,我创建了新报告github.com/vuejs/vue-next/issues/2907
  • 所以这听起来我们不需要卸载 vue 应用程序就可以对其进行垃圾收集?那么简单地删除 dom 根元素就足以将其标记为垃圾回收?还是我误会了。我将删除许多包含已安装的 vue 应用程序实例的 dom 元素,然后重新安装新的,所以很高兴知道这对内存的影响。

标签: vue.js memory-leaks vuejs3


【解决方案1】:

在 VUE 3.0.6 中修复

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
  • 2020-07-01
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
相关资源
最近更新 更多