$ emit导致内存泄漏-Vue.js

T. *_*ort 5 vue.js

我有两个组成部分。父母和孩子。父母根据加载孩子v-if。然后,孩子通过发送事件$emit来告诉父母删除孩子。

简而言之。

家长:

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

儿童:

template: 
<div>
    Some Content
    <div @click="$emit('close')">Close</div>
</div>
Run Code Online (Sandbox Code Playgroud)

问题是,当$emit('close')被触发时,将从DOM中删除该子项,但不会从内存中清除该组件。

如果父母使用该toggle按钮删除了孩子,它将从内存中清除孩子。

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

换句话说,似乎如果子级向父级发出信号,则应将其删除,然后将其保留在内存中。但是,如果父母将孩子直接删除(没有孩子的任何信号),则会从内存中删除它...

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

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

内存快照

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

单击切换后,将按预期创建VueComponent。 点击“切换”后

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

ski*_*tle 5

我有一个部分的解释。

似乎有些东西持有对最后一个被点击的 DOM 节点的引用。因此,如果您单击Close它,则会抓住对该文本节点的引用。文本节点的父节点是 ,<div>并且<div>有一个指向 Vue 组件的点击监听器。组件本身已经被销毁,它不能被 GCed。

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

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

我确实在没有 Vue 的情况下组合了一个页面来探索这个。我的实验表明,即使 Vue 不在页面上,也会发生对上次点击节点的引用。

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