移除渲染组件时的 Vue 内存泄漏

Vue memory leak when rendered components are removed

我在一个Vue应用中遇到内存泄漏,发生场景如下:

然而,分配的内存永远不会被释放,应用程序开始使用大约 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>

keep alive docs

我遇到了类似的问题。我通过属性传递给下一个组件的对象在我的例子中是复杂和大的,我不知道你是否也是这样?

我的问题已通过更改传递对象的方式得到解决。通过将 属性 更改为一个数字,在我的例子中是一个 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]
    }
}

当然,这个原则也需要应用于任何做同样事情的子组件,以防止子组件中的任何内存泄漏,这显然也会影响父组件。希望我能帮到你 :)