移除渲染组件时的 Vue 内存泄漏
Vue memory leak when rendered components are removed
我在一个Vue应用中遇到内存泄漏,发生场景如下:
- 我们有一个在包含许多子组件的 v-for 中呈现的组件
- 当从数组中删除相应的元素时,v-for 会重新呈现这些组件并正确删除与从数组中删除的元素相对应的组件。
然而,分配的内存永远不会被释放,应用程序开始使用大约 30-40 MB 的 RAM,在渲染 v-for 时增加到 200MB RAM(最终超过 1GB 并崩溃)添加更多元素或切换时浏览器)。当元素被移除时,它稳定地保持在 200MB(即使在手动垃圾收集时),所以它似乎保留了我的组件。
我尝试用堆快照定位问题,但它只显示一个子组件作为保留器。我找不到导致此组件未被垃圾回收的原因。我尝试使用 this.$root.off
取消订阅根目录下的所有事件监听器,但这似乎根本没有帮助...
代码本身是机密的,所以我不能只分享它,但是如果需要一些代码来理解这个问题,请告诉我,这样我就可以提供一个复制的例子。
有没有人知道如何解决这个问题,或者有什么想法可以找出内存泄漏的原因?
更新
这是在 v-for 中呈现组件的组件:
<template>
<b-tabs card class="tabMenu" v-model="index">
<b-tab v-for="(tab) in tabs" @click="doSomething" @change="doSomething">
<TabComponent :tab="tab"></TabComponent>
</b-tab>
</b-tabs>
</template>
<script>
import TabComponent from "./TabComponent";
export default {
components: {
TabComponent,
},
created: function () {
this.$root.$on("addTab", this.addTab);
},
data: function () {
return {
tabs: this.$store.state.tabs,
}
},
beforeDestroy: function(){
this.$root.$off("addTab");
},
methods: {
addTab(tab) {
this.$store.commit("addTab", {tab: tab});
},
}
};
</script>
以及它呈现的选项卡组件:
<template>
<div @mousedown.stop>
<!-- Other components are loaded here but not relevant -->
<div>
<div v-show="conditionA">
<resize-observer @notify="doSomething" v-if="conditionC"></resize-observer>
<!-- This component renders many SVG elements which can be found in the heapsnapshot as DetachedSvgElements when the parent is not present anymore -->
<VisualizationComponent v-show="conditionD"
:tab="tab"></VisualizationComponent>
</div>
</div>
</div>
</template>
<script>
export default {
components: {
},
props: {
tab: TabObject,
},
data: function () {
return {
}
},
watch: {
// Some watchers
},
mounted: function () {
this.$nextTick(function () {
// Do some calculations
this.$root.$emit("updateSomething");
});
},
created: function(){
this.$root.$on("listen", this.doSomething);
// And listen to more events
},
beforeDestroy: function(){
this.$root.$off("listen");
// And unsubscribe all others
},
computed: {
// Quite a lot of computed props
},
methods: {
// And also many methods for data processing
}
}
</script>
您正在命令 vue 使您的组件保持活动状态并询问它们为什么活动?!!!
解决方法:
只需将 max
prop 添加到 <keep-alive>
sudo-component 并传递 tabs.length
值将强制它丢弃已删除的。
<keep-alive :max="tabs.length">
...
</keep-alive>
我遇到了类似的问题。我通过属性传递给下一个组件的对象在我的例子中是复杂和大的,我不知道你是否也是这样?
我的问题已通过更改传递对象的方式得到解决。通过将 属性 更改为一个数字,在我的例子中是一个 ID,我能够在使用 属性 的组件中检索我的对象(基于 ID)。结果,我不必重复传递整个对象。出于某种原因,将大对象作为数据道具传递并不能正常工作并导致奇怪的行为...
在你的情况下,当你不将 'tab' 属性 直接传递给你的组件时,它可能会有所帮助,而是将此元素在商店中的位置索引然后获取它直接从组件中的商店。
因此您需要将 v-for 更改为:
<b-tab v-for="(tab, index) in tabs" @click="doSomething" @change="doSomething">
<keep-alive>
<TabComponent :tabIndex="index"></TabComponent>
</keep-alive>
</b-tab>
在您的 TabComponent 中:
props: {
tabIndex: Number,
},
data: function () {
return {
tab: this.$store.state.tabs[this.tabIndex]
}
}
当然,这个原则也需要应用于任何做同样事情的子组件,以防止子组件中的任何内存泄漏,这显然也会影响父组件。希望我能帮到你 :)
我在一个Vue应用中遇到内存泄漏,发生场景如下:
- 我们有一个在包含许多子组件的 v-for 中呈现的组件
- 当从数组中删除相应的元素时,v-for 会重新呈现这些组件并正确删除与从数组中删除的元素相对应的组件。
然而,分配的内存永远不会被释放,应用程序开始使用大约 30-40 MB 的 RAM,在渲染 v-for 时增加到 200MB RAM(最终超过 1GB 并崩溃)添加更多元素或切换时浏览器)。当元素被移除时,它稳定地保持在 200MB(即使在手动垃圾收集时),所以它似乎保留了我的组件。
我尝试用堆快照定位问题,但它只显示一个子组件作为保留器。我找不到导致此组件未被垃圾回收的原因。我尝试使用 this.$root.off
取消订阅根目录下的所有事件监听器,但这似乎根本没有帮助...
代码本身是机密的,所以我不能只分享它,但是如果需要一些代码来理解这个问题,请告诉我,这样我就可以提供一个复制的例子。
有没有人知道如何解决这个问题,或者有什么想法可以找出内存泄漏的原因?
更新
这是在 v-for 中呈现组件的组件:
<template>
<b-tabs card class="tabMenu" v-model="index">
<b-tab v-for="(tab) in tabs" @click="doSomething" @change="doSomething">
<TabComponent :tab="tab"></TabComponent>
</b-tab>
</b-tabs>
</template>
<script>
import TabComponent from "./TabComponent";
export default {
components: {
TabComponent,
},
created: function () {
this.$root.$on("addTab", this.addTab);
},
data: function () {
return {
tabs: this.$store.state.tabs,
}
},
beforeDestroy: function(){
this.$root.$off("addTab");
},
methods: {
addTab(tab) {
this.$store.commit("addTab", {tab: tab});
},
}
};
</script>
以及它呈现的选项卡组件:
<template>
<div @mousedown.stop>
<!-- Other components are loaded here but not relevant -->
<div>
<div v-show="conditionA">
<resize-observer @notify="doSomething" v-if="conditionC"></resize-observer>
<!-- This component renders many SVG elements which can be found in the heapsnapshot as DetachedSvgElements when the parent is not present anymore -->
<VisualizationComponent v-show="conditionD"
:tab="tab"></VisualizationComponent>
</div>
</div>
</div>
</template>
<script>
export default {
components: {
},
props: {
tab: TabObject,
},
data: function () {
return {
}
},
watch: {
// Some watchers
},
mounted: function () {
this.$nextTick(function () {
// Do some calculations
this.$root.$emit("updateSomething");
});
},
created: function(){
this.$root.$on("listen", this.doSomething);
// And listen to more events
},
beforeDestroy: function(){
this.$root.$off("listen");
// And unsubscribe all others
},
computed: {
// Quite a lot of computed props
},
methods: {
// And also many methods for data processing
}
}
</script>
您正在命令 vue 使您的组件保持活动状态并询问它们为什么活动?!!!
解决方法:
只需将 max
prop 添加到 <keep-alive>
sudo-component 并传递 tabs.length
值将强制它丢弃已删除的。
<keep-alive :max="tabs.length">
...
</keep-alive>
我遇到了类似的问题。我通过属性传递给下一个组件的对象在我的例子中是复杂和大的,我不知道你是否也是这样?
我的问题已通过更改传递对象的方式得到解决。通过将 属性 更改为一个数字,在我的例子中是一个 ID,我能够在使用 属性 的组件中检索我的对象(基于 ID)。结果,我不必重复传递整个对象。出于某种原因,将大对象作为数据道具传递并不能正常工作并导致奇怪的行为...
在你的情况下,当你不将 'tab' 属性 直接传递给你的组件时,它可能会有所帮助,而是将此元素在商店中的位置索引然后获取它直接从组件中的商店。
因此您需要将 v-for 更改为:
<b-tab v-for="(tab, index) in tabs" @click="doSomething" @change="doSomething">
<keep-alive>
<TabComponent :tabIndex="index"></TabComponent>
</keep-alive>
</b-tab>
在您的 TabComponent 中:
props: {
tabIndex: Number,
},
data: function () {
return {
tab: this.$store.state.tabs[this.tabIndex]
}
}
当然,这个原则也需要应用于任何做同样事情的子组件,以防止子组件中的任何内存泄漏,这显然也会影响父组件。希望我能帮到你 :)