【问题标题】:vue-router creates always a new Component instancevue-router 总是创建一个新的 Component 实例
【发布时间】:2018-08-31 15:29:54
【问题描述】:

我在 vue-router 中发现了一个让我很受触动的问题。 当我在我的路线之间切换时,总是会创建一个新的组件实例。此外,旧实例不会被删除,而是在后台运行!

我希望当我打开一个路由时,旧的组件会被销毁或停止运行。

是否有解决该问题的解决方法?

这是一个小提琴:https://jsfiddle.net/4xfa2f19/5885/

let foo = {
    template: '<div>Foo</div>',
    mounted() {
        console.log('Mount Foo with uid: ' + this._uid);
        setInterval(() => {console.log('Instance ' + this._uid + ' of Foo is running')}, 500);
    }
};

let bar = {
    template: '<div>Bar</div>',
    mounted() {
        console.log('Mount Bar with uid: ' + this._uid);
        setInterval(() => {console.log('Instance ' + this._uid + ' of Bar is running')}, 500);
    }
};


const router = new VueRouter({
    routes: [
        { path: '/user/foo', component: foo },
        { path: '/user/bar', component: bar }
    ]
});


const app = new Vue({ router }).$mount('#app');

【问题讨论】:

标签: javascript vue.js routing vue-router


【解决方案1】:

有两种方法可以解决这个问题:

destroy 挂钩中正确清理

如果您使用任何外部事件侦听器,例如 setIntervaladdEventListener 等,您还需要在组件被销毁时取消注册它们,例如:

{
    name: '...',
    template: '...',
    data() {
        return {
            interval: undefined,
            timeout: undefined
        };
    },
    mounted() {
        interval = setInterval(() => {console.log('Instance ' + this._uid + ' of myself is running')}, 500);
        timeout = setTimeout(() => {console.log('Instance ' + this._uid + ' of myself is running')}, 500);
        document.addEventListener('click', this.onOutsideClick);
    },
    beforeDestroy() {
        // Cleanup interval
        clearInterval(interval);
        // Cleanup any pending timeouts
        clearTimeout(timeout);
        // Cleanup any event listeners outside the root of the element
        document.removeEventListener('click', this.onOutsideClick);
    },
    methods: {
        onOutsideClick() {
            ...
        }
    }
}

使用 keep-alive 使组件保持活动状态

当使用 keepalive 时,Vue 会缓存你的组件,并在后台保持它处于活动状态,这意味着永远只会存在一个实例。如果您有大量路线,这可能会消耗更多内存

<keep-alive>
    <router-view></router-view>
</keep-alive>

【讨论】:

    【解决方案2】:

    当我在我的路由之间切换时,总是会创建一个新的组件实例。

    这是意料之中的。您可以通过&lt;keep-alive&gt; 组件保持实例处于活动状态并重新使用它们,但这通常不是必需的,如果是这样,需要特别注意重新启动所有需要的重用组件的本地状态。

    创建一个新实例更加简洁,因此是默认行为。

    此外,旧实例不会被删除,而是在后台运行!

    这不是预期的。先前的实例被销毁。

    setInterval(() => {console.log('Instance ' + this._uid + ' of Foo is running')}, 500);
    

    好吧,由于这个间隔回调包含对组件实例的引用,它不能被浏览器垃圾收集,所以你要让它们保持活动状态,而不是 Vue。

    如果没有那个间隔,我希望实例在路由器销毁它们后被垃圾收集。

    【讨论】:

    • 谢谢。有没有办法检查是否触发了“破坏”,以便我可以清除间隔?
    • 只需使用 Vue 的生命周期钩子即可。你已经知道mounted,所以你也知道beforeDestroyvuejs.org/v2/api/#beforeDestroy
    【解决方案3】:

    这里说明了同样的问题:https://github.com/vuejs/vuex/issues/1580

    作为一种解决方法,您可以使用过渡模式 out-in。罗兰达

    <transition mode="out-in">    <router-view></router-view> </transition>
    

    【讨论】:

      【解决方案4】:

      在 Vue 3 中,语法发生了变化。告诉 Vue Router 让所有组件保持活动状态的解决方案是:

      <router-view v-slot="{ Component }">
          <keep-alive>
              <component :is="Component" />
          </keep-alive>
      </router-view>
      

      如果你只想让某个人活着,那么你可以使用这个:

      <router-view v-slot="{ Component }">
          <keep-alive include="foo">
              <component :is="Component" />
          </keep-alive>
      </router-view>
      

      或者确保一个人没有活着(但所有其他人都活着),这样做:

      <router-view v-slot="{ Component }">
          <keep-alive exclude="foo">
              <component :is="Component" />
          </keep-alive>
      </router-view>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-05
        • 2013-10-20
        • 1970-01-01
        • 2012-06-20
        • 2023-03-31
        • 2021-11-25
        • 2019-01-14
        相关资源
        最近更新 更多