小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
TIP 👉 智者千虑,必有一失;愚者千虑,必有一得。《史记淮阴侯列传》
全局事件总线简介
全局事件总线(EventBus)是消息传递的一种方式,基于一个消息中心,订阅和发布消息的模式,在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件
事件类型
事件类型分为普通事件和ready事件
普通事件
普通事件使用vue的默认事件处理逻辑,需要先添加监听再出发事件,事件触发后添加的监听无法执行
- 1)监听事件
vm.$eventBus.$on('myEvent', () => {}) // 添加事件监听
vm.$eventBus.$once('myEvent', () => {}) // 添加事件监听,并且该监听只执行一次
- 2)触发监听
vm.$eventBus.$emit('myEvent', myArgument) // 触发事件,并且可以给监听器传递参数
- 3)移除监听
vm.$eventBus.$off('myEvent') // 移除事件的所有监听
ready事件
ready事件适用于事件触发时机不确定,但是事件监听必须执行的场景,事件触发前添加的监听和事件触发后添加的监听都会执行
- 1)监听事件
vm.$eventBus.$onReady('myEvent', () => {}) // 添加事件监听,并且该监听只执行一次
vm.$eventBus.$onReady('myEvent', () => {}, true) // 添加事件监听,并且该监听只执行一次
vm.$eventBus.$onReady('myEvent', () => {}, false) // 添加事件监听,并且该监听可以被多次触发
- 2)触发监听
vm.$eventBus.$emitReady('myEvent', myArgument) // 触发事件监听,并且可以给监听器传递参数
- 3)移除监听
vm.$eventBus.$removeReadyEventListener('myEvent', myEventHandler) // 触发事件监听,并且可以给监听器传递参数
- 4)清除事件
vm.$eventBus.$clearReadyEvent('myEvent') // 清除指定的事件
封装一个eventBusPlugin.js
import Vue from 'vue'
export default {
install () {
// 事件总线
const eventBus = new Vue()
/**
* ready事件监听
* @type {Object.<string, Array[Object.<string, any>]>}
* @desc 主键为事件名,值为事件监听对象数组
* 监听对象:
* 1) isOnce:表示该监听处理方法是否只执行一次,如果只执行一次,则执行后会从监听数组中移除
* 2) eventHandler:该监听处理方法
* 示例:{ isOnce: true, eventHandler: function (payload) { ... } }
*/
const readyListeners = {}
/**
* ready事件状态
* @type {Object.<string, Object.<string, any>>}
* @desc 主键为事件名,值为对象类型,表示当前的状态的对象
* 状态对象:
* 1) isReady: 改事件是否已触发
* 2) payload: 事件触发时的参数
* 示例:{ isReady: true, payload: {id: 12} }
*/
const readyStatus = {}
/**
* 添加ready事件监听方法(如果事件已触发过则直接执行)
* @param eventName 事件名
* @param eventHandler 事件监听执行方法,例:function (payload) { ... }
* @param isOnce 是否只执行一次(默认为true)
*/
function onReady (eventName, eventHandler, isOnce = true) {
// 事件状态
let status = readyStatus[eventName]
// 如果事件已触发过,则直接执行监听方法
if (status && status.isReady) {
// console.log('########### 直接执行了监听处理方法', eventHandler)
eventHandler(status.payload)
if (isOnce) { // 如果此监听只执行一次则不再添加到监听器数组中
return
}
}
/* 将监听对象添加到监听数组 */
let listeners = readyListeners[eventName]
if (!listeners) {
listeners = []
readyListeners[eventName] = listeners
}
listeners.push({
isOnce, // 是否只执行一次
eventHandler // 事件处理方法
})
}
/**
* 触发ready事件
* @param eventName 事件名
* @param payload 事件负载数据
*/
function emitReady (eventName, payload) {
// 设置事件状态
readyStatus[eventName] = { isReady: true, payload }
// 事件的监听数组
let listeners = readyListeners[eventName]
// 新监听数组(如果有只执行一次的监听器,执行后就移除)
let newListener = []
// 执行监听数组中的监听事件
if (listeners && listeners.length > 0) {
listeners.forEach((listener) => {
try {
if (!listener.isOnce) {
newListener.push(listener)
}
listener.eventHandler(payload)
} catch (e) {
console.error(`eventBus 的 ready 事件:${eventName},执行监听方法失败!`, e)
}
})
}
readyListeners[eventName] = newListener
}
/**
* 移除ready事件中的某一个监听方法
* @param eventName 事件名
* @param listener 事件监听方法
*/
function removeReadyEventListener (eventName, listener) {
if (eventName && listener) {
let eventListeners = readyListeners[eventName]
if (eventListeners) {
let index = -1
for (let i = 0; i < eventListeners.length; i++) {
let item = eventListeners[i]
if (item.eventHandler === listener) {
index = i
break
}
}
if (index !== -1) {
eventListeners.splice(index, 1)
}
}
}
}
/**
* 清除指定ready事件
* @param eventName 事件名
*/
function clearReadyEvent (eventName) {
delete readyListeners[eventName]
delete readyStatus[eventName]
}
eventBus.$readyListeners = readyListeners
eventBus.$readyStatus = readyStatus
eventBus.$onReady = onReady
eventBus.$emitReady = emitReady
eventBus.$removeReadyEventListener = removeReadyEventListener
eventBus.$clearReadyEvent = clearReadyEvent
Vue.prototype.$eventBus = eventBus
}
}
main.js
import Vue from 'vue'
import eventBusPlugin from 'eventBusPlugin.js'
// 注册全局事件总线
Vue.use(eventBusPlugin)