发布订阅模式:
定义对象间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新
事件总线是对发布订阅模式的一种实现,也是一种集中式事件处理机制,允许不同组件之间相互通信,而不需要相互依赖,以此来解耦
应用场景:公众号消息,短信提醒等等
传统的事件总线和发布订阅的实现
事件总线
const eventBus = {
// 保存类型和回调容器
callbacks: {
// login: [fn1, fn2]
}
};
// 绑定事件
eventBus.on = function (eventName, callback) {
// 判断有该类型事件
if (this.callbacks[eventName]) {
// 推入
this.callbacks[eventName].push(callback);
} else {
// 构造该类型数组
this.callbacks[eventName] = [callback];
}
};
// 触发事件
eventBus.emit = function (eventName, data) {
// 判断有该类型事件
if (this.callbacks[eventName]?.length > 0) {
// 遍历数组里函数执行传入数据
this.callbacks[eventName].forEach(event => event(data));
}
};
eventBus.off = function (eventName) {
// 如果传入事件名
if (eventName) {
// 删除指定事件
delete this.callbacks[eventName];
} else {
// 清空
this.callbacks = {};
}
};
// 测试
eventBus.on(“login”, data => {
console.log(用户已经登陆,数据${data}
);
});
eventBus.on(“logout”, data => {
console.log(用户已经退出,数据${data}
);
});
setTimeout(() => {
eventBus.emit(“login”, “云牧”);
eventBus.emit(“logout”, “云牧”);
}, 1000);
// eventBus.off(“login”);
// eventBus.off();
复制代码
发布订阅
const PubSub = {
id: 1,
callbacks: {
// pay:{
// // token_1: fn1,
// // token_2: fn2,
// }
}
};
PubSub.subscribe = function (channel, callback) {
// 唯一的编号
let token = “token_” + this.id++;
// 判断callbacks是否存在channel
if (this.callbacks[channel]) {
// 存入
this.callbacks[channel][token] = callback;
} else {
// 构造出对象存入
this.callbacks[channel] = {
[token]: callback
};
}
return token;
};
// 订阅频道
PubSub.publish = function (channel, data) {
// 获取当前频道所有的回调 遍历执行
if (this.callbacks[channel]) {
Object.values(this.callbacks[channel]).forEach(callback => callback(data));
}
};
// 取消订阅
PubSub.unsubscribe = function (flag) {
// 没有传则全部清空
if (!flag) {
this.callbacks = {};
// 判断
} else if (typeof flag === “string”) {
// 如果包含token_
if (flag.includes(“token_”)) {
// 遍历对象找到对应token
const callbackobj = Object.values(this.callbacks).find(obj => obj.hasOwnProperty(flag));
if (callbackobj) {
delete callbackobj[flag];
}
} else {
// 删除该订阅下所有回调
delete this.callbacks[flag];
}
}
};
// 测试
const id1 = PubSub.subscribe(“pay”, data => {
console.log(“商家接受到了订单”, data);
});
const id2 = PubSub.subscribe(“pay”, data => {
console.log(“骑手接受到了订单”, data);
});
const id3 = PubSub.subscribe(“cancel”, data => {
console.log(“买家取消了订单”, data);
});
// 取消了id1,商家无法接到订单
PubSub.unsubscribe(id1);
PubSub.publish(“pay”, {
title: “鱼香肉丝”,
price: 20,
address: “xxx”
});
PubSub.publish(“cancel”, {
title: “鱼香肉丝”,
price: 20,
address: “xxx”
});
四行实现发布订阅
原理:
表面是 window,根本是 EventTarget,window、document 和元素节点都是继承于 EventTarget
XMLHttpRequest、 WebSocket 也继承于 EventTarget
继承于它,就可以实现事件中心,可以 EventTarget.addEventListener() 、 EventTarget.removeEventListener() 、EventTarget.dispatchEvent
简化三行
升级八行
上面三行实现有诸多问题,比如不能多个实例、不能传递多参数、参数从 e.detail 获取不合理等
改进如下:
自定义事件
内置事件类型
点击按钮,这是 click 事件
输入框失焦,这是 blur 事件
鼠标滚动,这是 wheel 事件
触发内置事件
element[eventType] 直接调用
new [Event] + dispatchEvent
document.createElement(“a”).click();
// 自定义事件触发
const event = new MouseEvent(“click”);
document.createElement(“a”).dispatchEvent(event);
前端快捷生成 uuid
URL.createObjectURL(new Blob([“”])).split(“/”).pop()
自定义事件三种方式
document.createEvent()(废弃)
new Event()
new CustomEvent()
document.createEvent()
const event = document.createEvent(type);
new Event
event = new Event(type, eventInit);
可使用 new Event 通信,处理流程,达到解耦
开始吧new CustomEvent
event = new Event(type, eventInit);
相比之前方式,它可以携带更多的参数了:
兼容垫片(如果浏览器不支持 CustomEvent 的话):
(function () {
if (typeof CustomEvent !== “function”) {
var CustomEvent = function (event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
})();
这篇就写完了,大家如果觉得好的话可以多多点赞,赠人玫瑰,手有余香。我会继续努力奉献更高质量的文章的。