Vue.js 3 卸载和内存泄漏

Posted

技术标签:

【中文标题】Vue.js 3 卸载和内存泄漏【英文标题】:Vue.js 3 unmount and memory leak 【发布时间】: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。只需按照以下步骤操作

    运行代码 单击第一个按钮(.mount Vue 应用程序)。将出现“Vue 已安装”文本 打开 chrome 控制台并切换到“内存”选项卡 拍摄堆快照 单击第二个按钮(.unmount Vue 应用程序)。 “Vue 已安装”文本将消失 返回“内存”选项卡,点击“收集垃圾”(带有垃圾箱的图标) 获取第二个堆快照 切换到第一个快照并过滤“testClass”。 (您只会看到一个结果)。打开它并找到公共属性“vueApp”。在它旁边,您将找到存储在此属性中的对象的 @ID(例如 @567005) 切换到第二个快照并按 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 元素,然后重新安装新的,所以很高兴知道这对内存的影响。 【参考方案1】:

在 VUE 3.0.6 中修复

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

【讨论】:

详细解释在 GitHub 帖子这里:github.com/vuejs/vue-next/issues/2907 和 github.com/vuejs/vue-next/pull/2909

以上是关于Vue.js 3 卸载和内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

Vuex3.1.1更新:支持jsDelivr,修复内存泄漏

涉及卸载的共享对象的内存泄漏?

react中避免内存泄漏的方法

如何处理以设置间隔卸载组件以避免内存泄漏

android内存泄漏

linux内存泄漏怎么查