同一 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,所以事件的方向性和父关系很容易,只要所有通信的组件都是“提供”总线的组件的后代。
我有一个 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,所以事件的方向性和父关系很容易,只要所有通信的组件都是“提供”总线的组件的后代。