同一 Vue 应用程序的多个实例的唯一 EventBus

Unique EventBus for multiple instances of the same Vue application

我有一个 Vue 应用程序(基本上是一个视频播放器),它使用 EventBus 在通常无法轻松通信的组件之间进行通信。这在我开发视频播放器时非常有效,但现在我已经使用 Rollup 将其捆绑在一起,并且当我尝试将视频播放器的多个实例放在同一页面上时,一个实例发送的任何事件也将被所有应用程序的其他实例。

事后看来,我理解为什么人们似乎不喜欢 EventBus,但我找不到改进或替换它的好解决方案。我无法动态命名 EventBus 实例,因为那样我的应用程序的其余部分将不会被告知新名称。我什至不能在我的 EventBus 侦听器中使用类似 videoId 的东西来控制唯一性,因为如果视频在同一页面上不止一次,我会遇到同样的问题。

一些帖子建议使用 VueX,但我的应用程序不需要是有状态的,它似乎也不能替代事件和侦听器(尽管我在这方面可能是错的。)这似乎有很多开销比我需要更多的功能。再一次,我可能是错的。

我试图尽可能多地删除不相关的代码,但想了解一下我是如何实现我的 EventBus 的: event-bus.js:

import Vue from 'vue';

const EventBus = new Vue();

export default EventBus;

MediaPlayer.vue:

<template>
  <div>
    <div>
      <div id='media-player'>
        <end-screen v-if="videoIsFinished"/>
        <tap-video
          ref="tapVideoRef"
          :source='sourceUrl'
          :videoId='videoId'
          @videoEndHandler='videoEndHandler'
        >
        </tap-video>

        <div id="control-bar-container">
          <transition name="slide-fade">
            <div v-show='(showControls || !playing)' >
              <media-controls
                :playing="playing"
                :videoLength="videoLength"
              />
            </div>
          </transition>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import TapVideo from './TapVideo.vue';
import EventBus from './event-bus';

export default {
  data (){
    return{
      playing: false,
      showControls: false,
      videoLength: 0,
      tapVideoRef: null
    }
  },
  mounted() {
    this.tapVideoRef = this.$refs.tapVideoRef;

    EventBus.$on('videoLoaded', videoLength => {
      this.videoLength = videoLength;
    });

    EventBus.$on('playStateChange', playing => {
      this.onPlayStateChange(playing);
    });
  },
  beforeDestroy() {
    EventBus.$off(['playStateChange','closeDrawer']);
  },
  props: ['sourceUrl', 'platformType', 'videoId'],
}
</script>

万一有人遇到这个问题,我找到了一个适合我的解决方案。感谢@ChunbinLi 为我指明了正确的方向——他们的解决方案确实有效,但到处传递道具有点笨拙。

相反,我使用了 Vue 支持的 Provide/Inject 模式:https://v3.vuejs.org/guide/component-provide-inject.html

一些最小的相关代码:

最高级别的祖父母将提供EventBus,

Grandparent.vue

export default {
   provide() {
    return {
      eventBus: new Vue()
    }
  }
}

那么任何后代都有能力注入那个总线,

Parent.vue

export default {
  inject: ['eventBus'],
  created() {
    this.eventBus.$emit('neededEvent')
  }
}

Child.vue

export default {
  inject: ['eventBus'],
  created(){
    this.eventBus.$on('neededEvent', ()=>{console.log('Event triggered!')});
  }
}

这仍然是一个 GLOBAL EventBus,所以事件的方向性和父关系很容易,只要所有通信的组件都是“提供”总线的组件的后代。