已销毁的 Vue 组件的 Mixin 仍在监听事件

Posted

技术标签:

【中文标题】已销毁的 Vue 组件的 Mixin 仍在监听事件【英文标题】:Mixin for destroyed Vue component is still listening for events 【发布时间】:2020-11-09 11:57:47 【问题描述】:

我有一个父组件,它有条件地呈现两个子组件之一:

<template>
  <div>
    <!-- other code that changes conditional rendering -->

    <folders v-if="isSearchingInFolders" :key="1234"></folders>
    <snippets v-if="!isSearchingInFolders" :key="5678"></snippets>
  </div>
</template>

这些组件中的每一个都在本地使用相同的 mixin (searchMixin),如下所示:

<template>
  <div>
    <div>
      <snippet
        v-for="item in items"
        :snippet="item"
        :key="item.id">
      </snippet>
      <img v-if="busy" src="/icons/loader-grey.svg" >
    </div>
    <button @click="getItems">Get More</button>
  </div>
</template>

<script>
import searchMixin from './mixins/searchMixin';
import Snippet from './snippet';

export default 
  components:  Snippet ,

  mixins: [searchMixin],

  data() 
    return 
      resourceName: 'snippets'
    
  ,

</script>

每个组件在功能上都是等效的,但标记略有不同,因此在本示例中,文件夹可以替换为片段,反之亦然。

我使用的 mixin 看起来像这样(简化):

import axios from 'axios'
import   EventBus  from '../event-bus';

export default 

  data() 
    return 
      hasMoreItems: true,
      busy: false,
      items: []
    
  ,

  created() 
    EventBus.$on('search', this.getItems)
    this.getItems();
  ,

  destroyed() 
    this.$store.commit('resetSearchParams')
  ,

  computed: 
    endpoint() 
      return `/$this.resourceName/search`
    ,

    busyOrMaximum() 
      return this.busy || !this.hasMoreItems;
    
  ,

  methods: 
    getItems(reset = false) 
      <!-- get the items and add them to this.items -->
    
  

在父组件中,当我通过更改 isSearchingInFolders 变量来切换渲染时,预期的组件将被销毁并从 DOM 中删除(我已通过从 destroyed() 生命周期挂钩记录来检查这一点。但是 searchMixin被包含在那个组件中似乎没有被销毁并且仍然似乎在监听事件。这意味着当EventBus.$on('search', this.getItems)行在更改从父组件主动呈现后触发时,this.getItems()被触发两次。一次为文件夹和一次用于 sn-ps!

我期待组件的 mixin 与组件本身一起被销毁。我是否误解了组件销毁的工作原理?

【问题讨论】:

我还没有找到一个优雅的解决方案。然而,与此同时,我使用active: true 在mixin 数据中使用active 布尔切换构建了一个解决方法。然后在destroyed() 生命周期挂钩中,我使用this.active = false 将其设置为false。然后,在this.getItems() 方法 - if (this.active) do some stuff 中执行任何操作之前,我会检查 mixin/组件的活动状态。这有效并防止在不应该调用 mixin 操作时调用它们。但是,对此的任何见解将不胜感激! 应该有一种机制可以取消订阅该事件。这通常在 destroy 钩子中完成,如下所示:EventBus.$off('search', this.getItems) 啊哈!就是这个。非常感谢!所以这实际上是由全局可用的EventBus 仍在监听搜索事件引起的,而不是组件本身,对吗?如果您想将此添加为答案,我很乐意接受。 我添加了一个答案,其中还包括您在评论中的问题。 【参考方案1】:

是的,当您像传递事件处理程序一样传递事件处理程序时,EventBus 会保留对您传递到的函数的引用。这可以防止组件对象的破坏。因此,您需要通过调用EventBus.$off 清除引用,以便可以破坏组件。所以你的销毁事件钩子应该是这样的:

destroyed() 
    this.$store.commit('resetSearchParams')
    EventBus.$off('search', this.getItems)
  ,

【讨论】:

以上是关于已销毁的 Vue 组件的 Mixin 仍在监听事件的主要内容,如果未能解决你的问题,请参考以下文章

Angular 组件在被销毁后仍在监听订阅 [重复]

Vue中组件通信(eventBus)

vue创建监听事件和销毁监听事件-获取可视区域的高度

vue中destroyed应用于啥场景?

vue事件监听取消时遇见的问题

vue事件监听取消时遇见的问题