eventBus事件总线,用于从子组件向父组件发送消息,类似于消息发布--订阅模式。
// eventBus.js
class EventBus {
constructor() {
this.events = {} // 用于存放注册的事件,数据用键值对的方式存放
/**
key1: Array[fun1, fun2...] // 存放 key1事件注册的所有回调函数,
key2: Array[fun1, fun2...]
*/
// 别问,问就是使用对象存储数据结构比较灵活,方便数据的管理和操作
}
// 订阅消息方法
$on(event, callback) {
// 如果事件不存在,就注册新事件
// let ev = this.events[event] 这样写会有意想不到的结果
if (!this.events[event]) {
this.events[event] = []
}
// 存在就push回调函数数组中
this.events[event].push(callback)
}
// 发布消息方法
$emit(event, ...params) {
// 如果事件存在,依次遍历该事件下绑定的所有事件
if (this.events[event]) {
// 确保事件的完整性和一致性,循环遍历所有的事件
this.events[event].forEach(fn => fn(...params))
}
}
// 取消消息订阅方法
$off(event, callback) {
// 事件不存在直接return
if (!this.events[event]) return
// 如果没有传入callback,则默认删除该事件下所有的回调函数
if (!callback) {
delete this.events[event]
return
}
// 删除指定的callback
this.events[event] = this.events[event].filter(cb => cb !== callback)
}
}
// 使用方法
const eventBus = new EventBus()
// 发布消息
eventBus.$emit('sayHi', 'Jack')
// 订阅消息
eventBus.$on('sayHi', (name) => {})
// 取消订阅, callback可选
eventBus.$off('sayHi', [callback])
在Vue项目中使用,需要注意在父子组件通信中,不能分别使用import EventBus from 'eventBus.js',可能是因为在let eventBus = new EventBus()时,他们不是同一个实例。
// Person.vue
<template>
<List :eventBus="eventBus" />
</template>
<script setup>
import { onMounted } from 'vue'
import List from './components/List/index.vue'
import EventBus from './utils/eventBus.js'
const eventBus = new EventBus()
onMounted(() => { // 同时也要考虑父组件订阅消息的时机
eventBus.$on('getVal', (val) => {
console.log(val);
})
})
</script>
// List.vue
<template>
<button @click="onClick">向父组件发送信息</button>
</template>
<script setup>
const props = defineProps({
eventBus: {
type: Object,
required: true,
}
})
const onClick = () => {
props.eventBus.$emit('getVal', 'List 组件')
}
</script>
分享一些学习的笔记。🙂