问题
1、使用全局事件总线(EventBus)进行组件间通信时,由于项目多人协助开发,会产生自定义事件名命名冲突问题,请问有什么优雅的解决方案?
2、当同一个组件在同一个界面被多次复用,那该组件上使用$on监听自定义事件就会重复注册同一个事件问题,请问如何解决?
求大神指明思路,我想了很久,也百度谷歌了都未能解决
求大神指明思路,我想了很久,也百度谷歌了都未能解决
可以自已重写一个 $on 方法,然后在执行时先进行一次校验
const eventBus = new Vue()
const origin = eventBus.$on
eventBus.$on = function(eventName, ...args) {
// 在这里执行你想要的逻辑
origin.call(this, eventName, ...args)
}
问题一:
创建一个生成唯一 id 的 generator 函数 genEventName
,使用事件的几个模块引入同一个常量配置文件,配置文件 export 的事件名通过 genEventName()
生成(每次调用都是生成不同的 id)。
问题二:
每个 .vue
文件其实是一个模块,模块内定义一个单例的函数,将这个函数提供给 Vue 组件调用。
<script>
const bindEventOnce = (() => {
let isCalled = false;
return (vm) => {
if (isCalled) {
return;
}
isCalled = true;
vm.$on(/* ... */);
};
})();
export default {
created() {
bindEventOnce(this, /* ... */);
}
}
</script>
我的方案是采用类似目录路径的命名规范:
bus.on('/global/set/storage');
只要设计得当并且谨遵规范,那么相同的命名应该是指向同一个事件的,不会冲突,反而形成一种“默契”,当你要订阅一个什么事件的时候,不用查找发布方的信息,根据规则写就可以了。
$on
互不干扰。事件总线不具有 同组件链传递 的特性,该特性在普通父子组件、provide/inject中存在。
因此事件总线可以在 任意组件 中使用,例如组件a和组件b没有父子、孙子等等关系也可触发事件,只与on('key)、emit('key')中的 key 有关。
注:provide('key','value')、inject('key', '这是默认值')中的key在多个父子组件中不会相互影响,即使他们的key相同,只有组件a、组件b不存在父子、孙子等关系时才会冲突。
事件总线如果想实现同组件链中传递需要配合provide/inject提供唯一键:
//utils/event-bus/index
import mitt from 'mitt'
export const eventBus = mitt()
//父组件parent.vue
<template>
<div class="">
<slot></slot>
<button @click="reset">重置</button>
</div>
</template>
<script setup lang="ts">
import {provide} from 'vue';
import { eventBus } from '/@/utils/event-bus/index';
const name = Symbol();
provide('custom-name',name);
const reset = () => {
eventBus.emit(name);
}
</script>
//子组件child.vue
<template>
<div class="">
<input type="text" v-model="state.value">
</div>
</template>
<script setup lang="ts">
import { onMounted,onBeforeUnmount,inject,reactive } from "vue";
import { eventBus } from '/@/utils/event-bus/index';
const name: string = inject('custom-name','');
const state = reactive({
value: ''
})
const reset = () => {
state.value = "";
}
onMounted(() => {
eventBus.on(name, reset);
});
onBeforeUnmount(() => {
eventBus.off(name);
});
</script>
//任意页面中使用
<template>
<div class="">
<Parent>
<Child></Child>
</Parent>
<Parent>
<Child></Child>
</Parent>
</div>
</template>
<script setup lang="ts">
import Parent from './parent.vue';
import Child from './child.vue';
</script>
//顺便验证provide/inject同组件链转递数据的功能,以下写法provide/inject不生效
<template>
<div class="">
<Parent></Parent>
<Child></Child>
</div>
</template>
<script setup lang="ts">
import Parent from './parent.vue';
import Child from './child.vue';
</script>
10 回答11.3k 阅读
5 回答4.9k 阅读✓ 已解决
4 回答3.2k 阅读✓ 已解决
2 回答2.8k 阅读✓ 已解决
3 回答5.2k 阅读✓ 已解决
2 回答4.8k 阅读✓ 已解决
4 回答4.4k 阅读✓ 已解决