【问题标题】:$emit causing memory leak - Vue.js$emit 导致内存泄漏 - Vue.js
【发布时间】:2019-08-20 17:31:55
【问题描述】:

我有两个组件。一个父母和一个孩子。父级根据v-if 加载子级。然后孩子通过$emit发送一个事件告诉父母移除孩子。

简单地说。

家长:

data() {
    return {
        show: false
    }
},
// template: 
<div>
    <div @click="show = !show">Toggle</div>
    <child-component
        v-if="show"
        @close="show = false"
   />
</div>

孩子:

template: 
<div>
    Some Content
    <div @click="$emit('close')">Close</div>
</div>

问题是当$emit('close') 被触发时,子元素从DOM 中移除,但组件并没有从内存中清除。

如果父母使用toggle 按钮删除孩子,它确实会从内存中清除孩子。

我也尝试过使用Vuex store$root.data,但这也会导致内存泄漏。

换句话说,如果子进程向父进程发出信号,表明它应该被删除,它就会被保存在内存中。但是,如果父级直接删除子级(没有来自子级的任何信号),它就会从内存中删除...

任何想法为什么会发生这种情况以及我应该做些什么来防止这种内存泄漏?有必要让孩子发出将其移除的信号。

[编辑] - 代码笔中的演示。 https://codepen.io/tomshort5/pen/BaBLXvb

内存快照

通过在某些操作后制作内存快照可以最清楚地看到这一点。当页面加载时,我们有一个 Vue 实例。

点击切换后,会按预期创建一个 VueComponent。

通过事件触发child被移除后,VueComponent不会从内存中移除。将其与再次单击“切换”进行比较时,它确实显示该组件已从内存中删除。

【问题讨论】:

  • 是什么让你认为存在内存泄漏?
  • component is not cleared from memory 你是如何识别内存泄漏的?
  • 我无法用您提供的示例重现您的问题。它正确地显示了调用其被销毁的生命周期钩子的组件。 codesandbox.io/s/vue-template-ptpxc?fontsize=14 显然它只会在GC运行时清除内存,但从那时起内存就被清除了。
  • 我已经创建了一个 codepen,可以在这里重现:codepen.io/tomshort5/pen/BaBLXvb 在开发者控制台的内存选项卡中。拍摄快照并按“Vue”过滤
  • 这很奇怪。简短的回答是,由于某种原因,它仍然在第一个组件的 $children 列表中。长答案是:为什么?!

标签: vue.js


【解决方案1】:

我有部分解释。

似乎有东西在持有对最后一个被点击的 DOM 节点的引用。因此,如果您单击Close,它将获取对该文本节点的引用。文本节点的父节点是&lt;div&gt;&lt;div&gt; 有一个点击监听器指向 Vue 组件。组件本身已被销毁,无法被 GC。

如果您点击Toggle,它会保留对Toggle 文本节点的引用。由于该节点位于 DOM 树的不同部分,因此它不会保留对具有单击侦听器的元素的任何引用。 Vue组件GC成功。

我无法准确确定是什么保留了对上次单击节点的此引用。堆快照并不是特别有用。它只显示InternalNodewindow 连接到相关文本节点。

我确实在没有 Vue 的情况下整理了一个页面来探索这个。我的实验表明,即使 Vue 不在页面上,也会保留对最后点击节点的引用。

这里的内存泄漏只是非常暂时的。在页面上的任何位置再单击一次似乎就足以修复它。与我的理论一致,它更新了“最后点击的节点”引用,允许分离的 DOM 节点和 Vue 组件一起被 GC。

【讨论】:

    猜你喜欢
    • 2015-07-06
    • 2014-06-07
    • 2013-11-20
    • 2011-10-28
    • 2016-01-18
    • 2012-12-13
    • 1970-01-01
    • 2011-01-08
    • 2011-02-01
    相关资源
    最近更新 更多