原文:https://dushusir.com/js-event-bus/
介紹
Event Bus
事件總線岁诉,通常作為多個(gè)模塊間的通信機(jī)制跋选,相當(dāng)于一個(gè)事件管理中心前标,一個(gè)模塊發(fā)送消息,其它模塊接受消息只搁,就達(dá)到了通信的作用俭尖。
比如,Vue 組件間的數(shù)據(jù)傳遞可以使用一個(gè) Event Bus
來通信,也可以用作微內(nèi)核插件系統(tǒng)中的插件和核心通信缭付。
原理
Event Bus
本質(zhì)上是采用了發(fā)布-訂閱的設(shè)計(jì)模式陷猫,比如多個(gè)模塊 A
、B
足陨、C
訂閱了一個(gè)事件 EventX
墨缘,然后某一個(gè)模塊 X
在事件總線發(fā)布了這個(gè)事件零抬,那么事件總線會(huì)負(fù)責(zé)通知所有訂閱者 A
平夜、B
、C
玩裙,它們都能收到這個(gè)通知消息段直,同時(shí)還可以傳遞參數(shù)坷牛。
// 關(guān)系圖
模塊X
?發(fā)布EventX
╔════════════════════════════════════════════════════════════════════╗
║ Event Bus ║
║ ║
║ 【EventX】 【EventY】 【EventZ】 ... ║
╚════════════════════════════════════════════════════════════════════╝
?訂閱EventX ?訂閱EventX ?訂閱EventX
模塊A 模塊B 模塊C
分析
如何使用 JavaScript 來實(shí)現(xiàn)一個(gè)簡單版本的 Event Bus
- 首先構(gòu)造一個(gè)
EventBus
類京闰,初始化一個(gè)空對(duì)象用于存放所有的事件 - 在接受訂閱時(shí),將事件名稱作為 key 值俏站,將需要在接受發(fā)布消息后執(zhí)行的回調(diào)函數(shù)作為 value 值痊土,由于一個(gè)事件可能有多個(gè)訂閱者,所以這里的回調(diào)函數(shù)要存儲(chǔ)成列表
- 在發(fā)布事件消息時(shí)犯祠,從事件列表里取得指定的事件名稱對(duì)應(yīng)的所有回調(diào)函數(shù)衡载,依次觸發(fā)執(zhí)行即可
以下是代碼詳細(xì)實(shí)現(xiàn)痰娱,可以復(fù)制到谷歌瀏覽器控制臺(tái)直接運(yùn)行檢測效果。
代碼
class EventBus {
constructor() {
// 初始化事件列表
this.eventObject = {};
}
// 發(fā)布事件
publish(eventName) {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackList = this.eventObject[eventName];
if (!callbackList) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let callback of callbackList) {
callback();
}
}
// 訂閱事件
subscribe(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
this.eventObject[eventName] = [];
}
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
this.eventObject[eventName].push(callback);
}
}
// 測試
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", () => {
console.log("模塊A");
});
eventBus.subscribe("eventX", () => {
console.log("模塊B");
});
eventBus.subscribe("eventX", () => {
console.log("模塊C");
});
// 發(fā)布事件eventX
eventBus.publish("eventX");
// 輸出
> 模塊A
> 模塊B
> 模塊C
上面我們實(shí)現(xiàn)了最基礎(chǔ)的發(fā)布和訂閱功能鲸睛,實(shí)際應(yīng)用中官辈,還可能有更進(jìn)階的需求遍坟。
進(jìn)階
1. 如何在發(fā)送消息時(shí)傳遞參數(shù)
發(fā)布者傳入一個(gè)參數(shù)到 EventBus
中政鼠,在 callback
回調(diào)函數(shù)執(zhí)行的時(shí)候接著傳出參數(shù),這樣每一個(gè)訂閱者就可以收到參數(shù)了万搔。
代碼
class EventBus {
constructor() {
// 初始化事件列表
this.eventObject = {};
}
// 發(fā)布事件
publish(eventName, ...args) {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackList = this.eventObject[eventName];
if (!callbackList) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let callback of callbackList) {
// 執(zhí)行時(shí)傳入?yún)?shù)
callback(...args);
}
}
// 訂閱事件
subscribe(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
this.eventObject[eventName] = [];
}
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
this.eventObject[eventName].push(callback);
}
}
// 測試
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊A", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊B", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊C", obj, num);
});
// 發(fā)布事件eventX
eventBus.publish("eventX", { msg: "EventX published!" }, 1);
// 輸出
> 模塊A {msg: 'EventX published!'} 1
> 模塊B {msg: 'EventX published!'} 1
> 模塊C {msg: 'EventX published!'} 1
2. 訂閱后如何取消訂閱
有時(shí)候訂閱者只想在某一個(gè)時(shí)間段訂閱消息瞬雹,這就涉及帶取消訂閱功能酗捌。我們將對(duì)代碼進(jìn)行改造涌哲。
首先阀圾,要實(shí)現(xiàn)指定訂閱者取消訂閱,每一次訂閱事件時(shí)涡真,都生成唯一一個(gè)取消訂閱的函數(shù),用戶直接調(diào)用這個(gè)函數(shù)缸剪,我們就把當(dāng)前訂閱的回調(diào)函數(shù)刪除橄登。
// 每一次訂閱事件讥此,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
};
其次萄喳,訂閱的回調(diào)函數(shù)列表使換成對(duì)象結(jié)構(gòu)存儲(chǔ)蹋半,為每一個(gè)回調(diào)函數(shù)設(shè)定一個(gè)唯一 id
减江, 注銷回調(diào)函數(shù)的時(shí)候可以提高刪除的效率,如果還是使用數(shù)組的話需要使用 split
刪除份企,效率不如對(duì)象的 delete
巡莹。
代碼
class EventBus {
constructor() {
// 初始化事件列表
this.eventObject = {};
// 回調(diào)函數(shù)列表的id
this.callbackId = 0;
}
// 發(fā)布事件
publish(eventName, ...args) {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackObject = this.eventObject[eventName];
if (!callbackObject) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let id in callbackObject) {
// 執(zhí)行時(shí)傳入?yún)?shù)
callbackObject[id](...args);
}
}
// 訂閱事件
subscribe(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)降宅,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this.eventObject[eventName] = {};
}
const id = this.callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增腰根,供下一個(gè)回調(diào)函數(shù)使用
this.eventObject[eventName][id] = callback;
// 每一次訂閱事件,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了瘸恼,也把整個(gè)事件對(duì)象清除
if (Object.keys(this.eventObject[eventName]).length === 0) {
delete this.eventObject[eventName];
}
};
return { unSubscribe };
}
}
// 測試
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊A", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊B", obj, num);
});
const subscriberC = eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊C", obj, num);
});
// 發(fā)布事件eventX
eventBus.publish("eventX", { msg: "EventX published!" }, 1);
// 模塊C取消訂閱
subscriberC.unSubscribe();
// 再次發(fā)布事件eventX钞脂,模塊C不會(huì)再收到消息了
eventBus.publish("eventX", { msg: "EventX published again!" }, 2);
// 輸出
> 模塊A {msg: 'EventX published!'} 1
> 模塊B {msg: 'EventX published!'} 1
> 模塊C {msg: 'EventX published!'} 1
> 模塊A {msg: 'EventX published again!'} 2
> 模塊B {msg: 'EventX published again!'} 2
3. 如何只訂閱一次
如果一個(gè)事件只發(fā)生一次冰啃,通常也只需要訂閱一次,收到消息后就不用再接受消息焚刚。
首先扇调,我們提供一個(gè) subscribeOnce
的接口狼钮,內(nèi)部實(shí)現(xiàn)幾乎和 subscribe
一樣熬芜,只有一個(gè)地方有區(qū)別,在 callbackId
前面的加一個(gè)字符 d
瑞侮,用來標(biāo)示這是一個(gè)需要?jiǎng)h除的訂閱鼓拧。
// 標(biāo)示為只訂閱一次的回調(diào)函數(shù)
const id = "d" + this.callbackId++;
然后季俩,在執(zhí)行回調(diào)函數(shù)后判斷當(dāng)前回調(diào)函數(shù)的 id
有沒有標(biāo)示,決定我們是否需要?jiǎng)h除這個(gè)回調(diào)函數(shù)藐鹤。
// 只訂閱一次的回調(diào)函數(shù)需要?jiǎng)h除
if (id[0] === "d") {
delete callbackObject[id];
}
代碼
class EventBus {
constructor() {
// 初始化事件列表
this.eventObject = {};
// 回調(diào)函數(shù)列表的id
this.callbackId = 0;
}
// 發(fā)布事件
publish(eventName, ...args) {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackObject = this.eventObject[eventName];
if (!callbackObject) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let id in callbackObject) {
// 執(zhí)行時(shí)傳入?yún)?shù)
callbackObject[id](...args);
// 只訂閱一次的回調(diào)函數(shù)需要?jiǎng)h除
if (id[0] === "d") {
delete callbackObject[id];
}
}
}
// 訂閱事件
subscribe(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)娱节,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this.eventObject[eventName] = {};
}
const id = this.callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增肄满,供下一個(gè)回調(diào)函數(shù)使用
this.eventObject[eventName][id] = callback;
// 每一次訂閱事件质涛,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了汇陆,也把整個(gè)事件對(duì)象清除
if (Object.keys(this.eventObject[eventName]).length === 0) {
delete this.eventObject[eventName];
}
};
return { unSubscribe };
}
// 只訂閱一次
subscribeOnce(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ),注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this.eventObject[eventName] = {};
}
// 標(biāo)示為只訂閱一次的回調(diào)函數(shù)
const id = "d" + this.callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增阅羹,供下一個(gè)回調(diào)函數(shù)使用
this.eventObject[eventName][id] = callback;
// 每一次訂閱事件捏鱼,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了,也把整個(gè)事件對(duì)象清除
if (Object.keys(this.eventObject[eventName]).length === 0) {
delete this.eventObject[eventName];
}
};
return { unSubscribe };
}
}
// 測試
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊A", obj, num);
});
eventBus.subscribeOnce("eventX", (obj, num) => {
console.log("模塊B", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊C", obj, num);
});
// 發(fā)布事件eventX
eventBus.publish("eventX", { msg: "EventX published!" }, 1);
// 再次發(fā)布事件eventX,模塊B只訂閱了一次看尼,不會(huì)再收到消息了
eventBus.publish("eventX", { msg: "EventX published again!" }, 2);
// 輸出
> 模塊A {msg: 'EventX published!'} 1
> 模塊C {msg: 'EventX published!'} 1
> 模塊B {msg: 'EventX published!'} 1
> 模塊A {msg: 'EventX published again!'} 2
> 模塊C {msg: 'EventX published again!'} 2
4. 如何清除某個(gè)事件或者所有事件
我們還希望通過一個(gè) clear
的操作來將指定事件的所有訂閱清除掉藏斩,這個(gè)通常在一些組件或者模塊卸載的時(shí)候用到灾茁。
// 清除事件
clear(eventName) {
// 未提供事件名稱谷炸,默認(rèn)清除所有事件
if (!eventName) {
this.eventObject = {};
return;
}
// 清除指定事件
delete this.eventObject[eventName];
}
和取消訂閱的邏輯相似旬陡,只不過這里統(tǒng)一處理了。
代碼
class EventBus {
constructor() {
// 初始化事件列表
this.eventObject = {};
// 回調(diào)函數(shù)列表的id
this.callbackId = 0;
}
// 發(fā)布事件
publish(eventName, ...args) {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackObject = this.eventObject[eventName];
if (!callbackObject) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let id in callbackObject) {
// 執(zhí)行時(shí)傳入?yún)?shù)
callbackObject[id](...args);
// 只訂閱一次的回調(diào)函數(shù)需要?jiǎng)h除
if (id[0] === "d") {
delete callbackObject[id];
}
}
}
// 訂閱事件
subscribe(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)驶睦,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this.eventObject[eventName] = {};
}
const id = this.callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增场航,供下一個(gè)回調(diào)函數(shù)使用
this.eventObject[eventName][id] = callback;
// 每一次訂閱事件廉羔,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了憋他,也把整個(gè)事件對(duì)象清除
if (Object.keys(this.eventObject[eventName]).length === 0) {
delete this.eventObject[eventName];
}
};
return { unSubscribe };
}
// 只訂閱一次
subscribeOnce(eventName, callback) {
// 初始化這個(gè)事件
if (!this.eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)竹挡,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this.eventObject[eventName] = {};
}
// 標(biāo)示為只訂閱一次的回調(diào)函數(shù)
const id = "d" + this.callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增,供下一個(gè)回調(diào)函數(shù)使用
this.eventObject[eventName][id] = callback;
// 每一次訂閱事件梯码,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this.eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了忍些,也把整個(gè)事件對(duì)象清除
if (Object.keys(this.eventObject[eventName]).length === 0) {
delete this.eventObject[eventName];
}
};
return { unSubscribe };
}
// 清除事件
clear(eventName) {
// 未提供事件名稱,默認(rèn)清除所有事件
if (!eventName) {
this.eventObject = {};
return;
}
// 清除指定事件
delete this.eventObject[eventName];
}
}
// 測試
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊A", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊B", obj, num);
});
eventBus.subscribe("eventX", (obj, num) => {
console.log("模塊C", obj, num);
});
// 發(fā)布事件eventX
eventBus.publish("eventX", { msg: "EventX published!" }, 1);
// 清除
eventBus.clear("eventX");
// 再次發(fā)布事件eventX廓握,由于已經(jīng)清除隙券,所有模塊都不會(huì)再收到消息了
eventBus.publish("eventX", { msg: "EventX published again!" }, 2);
// 輸出
> 模塊A {msg: 'EventX published!'} 1
> 模塊B {msg: 'EventX published!'} 1
> 模塊C {msg: 'EventX published!'} 1
> eventX not found!
5. TypeScript 版本
鑒于現(xiàn)在 TypeScript 已經(jīng)被大規(guī)模采用闹司,尤其是大型前端項(xiàng)目游桩,我們簡要的改造為一個(gè) TypeScript 版本
可以復(fù)制以下代碼到 TypeScript Playground 體驗(yàn)運(yùn)行效果
代碼
interface ICallbackList {
[id: string]: Function;
}
interface IEventObject {
[eventName: string]: ICallbackList;
}
interface ISubscribe {
unSubscribe: () => void;
}
interface IEventBus {
publish<T extends any[]>(eventName: string, ...args: T): void;
subscribe(eventName: string, callback: Function): ISubscribe;
subscribeOnce(eventName: string, callback: Function): ISubscribe;
clear(eventName: string): void;
}
class EventBus implements IEventBus {
private _eventObject: IEventObject;
private _callbackId: number;
constructor() {
// 初始化事件列表
this._eventObject = {};
// 回調(diào)函數(shù)列表的id
this._callbackId = 0;
}
// 發(fā)布事件
publish<T extends any[]>(eventName: string, ...args: T): void {
// 取出當(dāng)前事件所有的回調(diào)函數(shù)
const callbackObject = this._eventObject[eventName];
if (!callbackObject) return console.warn(eventName + " not found!");
// 執(zhí)行每一個(gè)回調(diào)函數(shù)
for (let id in callbackObject) {
// 執(zhí)行時(shí)傳入?yún)?shù)
callbackObject[id](...args);
// 只訂閱一次的回調(diào)函數(shù)需要?jiǎng)h除
if (id[0] === "d") {
delete callbackObject[id];
}
}
}
// 訂閱事件
subscribe(eventName: string, callback: Function): ISubscribe {
// 初始化這個(gè)事件
if (!this._eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)借卧,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this._eventObject[eventName] = {};
}
const id = this._callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增铐刘,供下一個(gè)回調(diào)函數(shù)使用
this._eventObject[eventName][id] = callback;
// 每一次訂閱事件,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this._eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了檩禾,也把整個(gè)事件對(duì)象清除
if (Object.keys(this._eventObject[eventName]).length === 0) {
delete this._eventObject[eventName];
}
};
return { unSubscribe };
}
// 只訂閱一次
subscribeOnce(eventName: string, callback: Function): ISubscribe {
// 初始化這個(gè)事件
if (!this._eventObject[eventName]) {
// 使用對(duì)象存儲(chǔ)盼产,注銷回調(diào)函數(shù)的時(shí)候提高刪除的效率
this._eventObject[eventName] = {};
}
// 標(biāo)示為只訂閱一次的回調(diào)函數(shù)
const id = "d" + this._callbackId++;
// 存儲(chǔ)訂閱者的回調(diào)函數(shù)
// callbackId使用后需要自增勺馆,供下一個(gè)回調(diào)函數(shù)使用
this._eventObject[eventName][id] = callback;
// 每一次訂閱事件谓传,都生成唯一一個(gè)取消訂閱的函數(shù)
const unSubscribe = () => {
// 清除這個(gè)訂閱者的回調(diào)函數(shù)
delete this._eventObject[eventName][id];
// 如果這個(gè)事件沒有訂閱者了,也把整個(gè)事件對(duì)象清除
if (Object.keys(this._eventObject[eventName]).length === 0) {
delete this._eventObject[eventName];
}
};
return { unSubscribe };
}
// 清除事件
clear(eventName: string): void {
// 未提供事件名稱紧卒,默認(rèn)清除所有事件
if (!eventName) {
this._eventObject = {};
return;
}
// 清除指定事件
delete this._eventObject[eventName];
}
}
// 測試
interface IObj {
msg: string;
}
type PublishType = [IObj, number];
const eventBus = new EventBus();
// 訂閱事件eventX
eventBus.subscribe("eventX", (obj: IObj, num: number, s: string) => {
console.log("模塊A", obj, num);
});
eventBus.subscribe("eventX", (obj: IObj, num: number) => {
console.log("模塊B", obj, num);
});
eventBus.subscribe("eventX", (obj: IObj, num: number) => {
console.log("模塊C", obj, num);
});
// 發(fā)布事件eventX
eventBus.publish("eventX", { msg: "EventX published!" }, 1);
// 清除
eventBus.clear("eventX");
// 再次發(fā)布事件eventX跑芳,由于已經(jīng)清除博个,所有模塊都不會(huì)再收到消息了
eventBus.publish<PublishType>("eventX", { msg: "EventX published again!" }, 2);
// 輸出
[LOG]: "模塊A", {
"msg": "EventX published!"
}, 1
[LOG]: "模塊B", {
"msg": "EventX published!"
}, 1
[LOG]: "模塊C", {
"msg": "EventX published!"
}, 1
[WRN]: "eventX not found!"
6. 單例模式
在實(shí)際使用過程中,往往只需要一個(gè)事件總線就能滿足需求往堡,這里有兩種情況共耍,保持在上層實(shí)例中單例和全局單例。
- 保持在上層實(shí)例中單例
將事件總線引入到上層實(shí)例使用穆咐,只需要保證在一個(gè)上層實(shí)例中只有一個(gè) EventBus
字旭,如果上層實(shí)例有多個(gè),意味著有多個(gè)事件總線拍柒,但是每個(gè)上層實(shí)例管控自己的事件總線斤儿。
首先在上層實(shí)例中建立一個(gè)變量用來存儲(chǔ)事件總線恐锦,只在第一次使用時(shí)初始化疆液,后續(xù)其他模塊使用事件總線時(shí)直接取得這個(gè)事件總線實(shí)例堕油。
代碼
// 上層實(shí)例
class LWebApp {
private _eventBus?: EventBus;
constructor() {}
public getEventBus() {
// 第一次初始化
if (this._eventBus == undefined) {
this._eventBus = new EventBus();
}
// 后續(xù)每次直接取唯一一個(gè)實(shí)例掉缺,保持在LWebApp實(shí)例中單例
return this._eventBus;
}
}
// 使用
const eventBus = new LWebApp().getEventBus();
- 全局單例
有時(shí)候我們希望不管哪一個(gè)模塊想使用我們的事件總線,我們都想這些模塊使用的是同一個(gè)實(shí)例艰毒,這就是全局單例搜囱,這種設(shè)計(jì)能更容易統(tǒng)一管理事件。
寫法同上面的類似绊汹,區(qū)別是要把 _eventBus
和 getEventBus
轉(zhuǎn)為靜態(tài)屬性西乖。使用時(shí)無需實(shí)例化 EventBusTool
工具類,直接使用靜態(tài)方法就行了荒叼。
代碼
// 上層實(shí)例
class EventBusTool {
private static _eventBus?: EventBus;
constructor() {}
public static getEventBus(): EventBus {
// 第一次初始化
if (this._eventBus == undefined) {
this._eventBus = new EventBus();
}
// 后續(xù)每次直接取唯一一個(gè)實(shí)例被廓,保持全局單例
return this._eventBus;
}
}
// 使用
const eventBus = EventBusTool.getEventBus();
原文:https://dushusir.com/js-event-bus/
總結(jié)
以上是小編對(duì) Event Bus
的一些理解嫁乘,基本上實(shí)現(xiàn)了想要的效果球碉。通過自己動(dòng)手實(shí)現(xiàn)一遍發(fā)布訂閱模式睁冬,也加深了對(duì)經(jīng)典設(shè)計(jì)模式的理解。其中還有很多不足和需要優(yōu)化的地方直奋,歡迎大家多多分享自己的經(jīng)驗(yàn)施禾。