EventBus—事件總線

2021-03-02 Geeker工作坊
EventBus 概要

EventBus是消息傳遞的一種方式,基於一個消息中心,訂閱和發布消息的模式。Vue中就有一個消息總線的機制。當然消息總線不僅僅局限於WEB前端,Android 、IOS中的消息中心也是如此實現的。

[1] 設計模式:訂閱者發布者模式。

[2] API的設計:

1. 只能構造一個消息對象。

2. on('msgName', func) 訂閱消息,msgName:訂閱的消息名稱 ;func: 訂閱的消息,會在執行$emit 後調用。

3. once('msgName', func) 僅訂閱一次消息,後訂閱的會替換前面訂閱的消息。

4. emit('msgName', msg) 發布消息 msgName:消息名稱;msg:可選 發布的消息,傳遞給監聽函數的參數。

5. off('msgName') 移除消息。

Vue EventBus 源碼解析

VUE中EventBus可以用來進行任何組件之間的通信。EventBus可以當成一個管道,這個管道兩端可以接好多組件,兩端的任何一個組件都可以進行通信。這個管道就是Vue實例,實例中有四個跟事件派發相關的方法$on $off $emit $once。API的設計跟上面提到的一樣,不過它做了一些增強。

首先總體上介紹一下Vue EventBus的原理:

[1] Vue在實例化的時候會進行很多初始化的操作,其中包括 eventMixin 方法,該方法裡包含了上面提及的四個方法。

[2] 四個方法的實現原理如下:

vue實例上會創建一個對象來保存所有要監聽的事件:vm._events = {}

每當我們要監聽一個事件,就往vm.events裡添加一個鍵值對,事件的名稱作為鍵,一個空數組作為值。例如我們要監聽的事件名稱為event1,則vm._events = {event1: []}

監聽事件的回調函數都會添加到對應的數組中,例如我們調用

vm.$on('event1', cb1);
vm.$on('event1', cb2);
vm.$on('event1', cb3);

則此時vm._events={event1: [cb1, cb2, cb3]}

當調用移除監聽事件的方法時,所做的操作為移除其對應數組裡的回調函數,例如當我們調用

vm.$off('event1', cb1);

這時cb1就被移除了,vm._events={event1: [cb2, cb3]}

當我們執行$emit觸發對應事件時,所做的操作就是把該事件對應數組裡的回調函數都拿出來執行一遍,例如當我們調用

vm.$emit('event1')

這時候會取出event1對應數組裡的cb2和cb3執行

$once表示該事件只會觸發執行一次,後面在觸發就沒用了,例如當我們調用:

// 用$on方法監聽event2,回調函數為cb4
vm.$on('event2', cb4);

// 用$once方法監聽event2,回調函數為cb5
vm.$once('event2', cb5);

// 觸發event2事件,會執行cb4和cb5
vm.$emit('event2');

// 再次觸發event2事件,這裡只會執行cb4,不會執行cb5,cb5隻會執行一次
vm.$emit('event2');

以上即為事件派發方法的基本原理,當然這些方法還有一些稍微複雜一點的使用方式,比如

$on(['event1', 'event2'], cb) // 監聽多個事件

$off(['event1', 'event2'], cb) // 移除多個事件

$off() // 移除事件不傳參數

$off('event1') // 移除事件傳一個參數

$off('event1', cb) // 移除事件傳兩個參數

$emit('event1', param1, param2) // 觸發事件傳參數

看完下面的源碼解析就知道這些情況都是怎麼處理的了。


function eventsMixin(Vue) { Vue.prototype.$on = function(event, fn) {} Vue.prototype.$once = function(event, fn) {} Vue.prototype.$off = function(event, fn) {} Vue.prototype.$emit = function(event, fn) {}}Vue.prototype.$on = function(event, fn) { const vm = this if (Array.isArray(event)) { for (let i = 0, l = event.length; i < l; i++) { vm.$on(event[i], fn); } } else { (vm._events[event] || (vm._events[event] = [])).push(fn); } return vm}Vue.prototype.$emit = function(event) { const vm = this; const cbs = vm._events[event]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; const args = toArray(arguments, 1); for (let i = 0, l = cbs.length; i < l; i++) { try { cbs[i].apply(vm, args); } catch(e) { handleError(e, vm, ("event handler for \"" + event + "\"")); } } } return vm;}Vue.prototype.$once = function(event, fn) { const vm = this function on() { vm.$off(event, on); fn.apply(vm, arguments); } on.fn = fn; vm.$on(event, on); return vm;}Vue.prototype.$off = function(event, fn) { const vm = this; if (!arguments.length) { vm._events = Object.create(null); return vm; } if (Array.isArray(event)) { for (let i = 0, l = event.length; i < l; i++) { vm.$off(event[i], fn); } return vm; } const cbs = vm._events[event] if (!cbs) { return vm; } if (!fn) { vm._events[event] = null return vm; } let cb; let i = cbs.length; while (i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { cbs.splice(i, 1); break; } } return vm;}

自己實現一個EventBus

按照最初的API設計和參考Vue EventBus創建一個自己的EventBus。這個沒有支持批量的發布和訂閱事件。

class MyEventBus {    constructor() {                this.msgQueue = {};    }    on(event, fn) {        if (this.msgQueue.hasOwnProperty(event)) {            if (this.msgQueue[event] === 'function') {                this.msgQueue[event] = [this.msgQueue[event], fn];            } else {                this.msgQueue[event] = [...this.msgQueue[event], fn];            }        } else {                        this.msgQueue[event] = fn;        }    }        once(event, fn) {                this.msgQueue[event] = fn;    }        emit(event, ...msg) {        if (!this.msgQueue.hasOwnProperty(event)) return;        try {            if (typeof this.msgQueue[event] === 'function') {                this.msgQueue[event](...msg);            } else {                this.msgQueue[event].map(fn = >{                    fn(...msg)                })            }        } catch(e) {            console.log(e);        }    }        off(event) {        if (!this.msgQueue.hasOwnProperty(event)) return;        delete this.msgQueue[event]    }}
const EventBus = new MyEventBus();EventBus.on('event1', (...msg) = >{ console.log('訂閱的消息是', msg)});EventBus.emit('event1', 'hello world', '123');EventBus.off('event1');EventBus.emit('event1', 'hello2world');

小結

本次分享首先介紹了EventBus原理和API設計,然後介紹了VUE中EventBus實現方式,最後自己實現了一個EventBus。

相關焦點

  • 事件總線(Event Bus)知多少
    引言事件總線這個概念對你來說可能很陌生,但提到觀察者(發布-訂閱)模式,你也許就很熟悉。事件總線是對發布-訂閱模式的一種實現。它是一種集中式事件處理機制,允許不同的組件之間進行彼此通信而又不需要相互依賴,達到一種解耦的目的。我們來看看事件總線的處理流程:了解了事件總線的基本概念和處理流程,下面我們就來分析下如何去實現事件總線。
  • 你不可不知的前端進階知識:Vue事件總線(EventBus)使用詳細介紹
    如果咱們的應用程式不需要類似Vuex這樣的庫來處理組件之間的數據通信,就可以考慮Vue中的 事件總線 ,即 **EventBus**來通信。EventBus 又稱為事件總線。首先需要創建事件總線並將其導出,以便其它模塊可以使用或者監聽它。我們可以通過兩種方式來處理。先來看第一種,新創建一個 .js 文件,比如 event-bus.js
  • Vue組件的通信--eventBus
    但是對於具有簡單體系結構的應用程式來說,使用事件在組件之間進行通信就足夠了。為此,我們可以創建一個快速的解決方案並實現EventBus,也稱作vue的中央事件總線,適用於跨級或兄弟組件。EventBus允許我們在一個組件中發出一個事件,而在另一個組件中偵聽該事件。本示例將說明如何在Vue.js應用程式中執行此操作。
  • 前端技術:開發一個vue中央事件總線插件vue-bus
    大家都知道,一個中央事件總線bus,可以作為一個簡單的組件傳遞數據,用於解決跨級和兄弟組件通信問題,那麼,這篇文字,我將使用這種思想,將bus封裝為一個Vue的插件,可以在所有的組件間任意使用,而不需要導入bus。
  • vue中的eventBus會產生內存洩漏嗎
    :及時調用$offeventBus在vue中的實現eventBus是事件總線的意思,它本質上是一個發布訂閱者實現,在vue2.X中,vue實例上提供了$on,$emit,$off這三個方法,分別用來添加觀察者,發布事件,取消訂閱這三個操作。
  • 新能源汽車CAN總線Bus Off處理流程
    目前新能源汽車整車網絡還是依靠最為成熟的CAN-bus架構,工程師們通過CAN總線讀取車上的車速、轉速等信號可以控制整車上眾多的ECU單元。但是,你知道CAN總線Bus Off會對新能源汽車的功能帶來哪些影響嗎?本文來做詳細分析。為什麼新能源汽車行業離不開CAN總線?
  • GEMAC CAN總線測試分析儀
    ,總線流量負載,錯誤幀計數器    Bus status, bus traffic load, error frame counter v用戶友好的協議監視器(CAN,CANopen,SAE J1939)      User-friendly protocol monitor (CAN, CANopen, SAE J1939)
  • 近義詞組:事件 accident, incident, event, affair
    這裡incident更加泛指不愉快,不尋常的事件,大的小的,有意無意的。【without incident】 平安無事的 ---event, 名詞, 事件,活動(anything that happens, especially something important or unusual)-This year's Olympic Games will be the biggest
  • usb總線驅動粗略過程(OHCI協議)
    usb總線驅動分類:UHCI: intel公司製作 適用於低速/全速設備OHCI全速設備EHCI: 高速設備(48M)2410裡面的usb總線
  • 基於I2C總線性能問答集錦
    2. each devive has its own device ID and I2C bus will confirm the ID before read and write[問:carelisan] IIC總線與RS232,RS458有怎樣的不同?
  • RT-Thread設備框架使用指南——I2C 總線設備
    I2C 簡介I2C(Inter Integrated Circuit)總線是 PHILIPS 公司開發的一種半雙工、雙向二線制同步串行總線。I2C 總線傳輸數據時只需兩根信號線,一根是雙向數據線 SDA(serial data),另一根是雙向時鐘線 SCL(serial clock)。
  • Node.js 的 EventEmitter 事件處理詳解
    在事件驅動的編程中,事件(event) 是一個或多個動作的結果,這可能是用戶的操作或者傳感器的定時輸出等。我們可以把事件驅動程序看作是發布-訂閱模型,其中發布者觸發事件,訂閱者偵聽事件並採取相應的措施。
  • 精講CAN總線通信---CAN總線拓撲結構、CAN控制器和收發器、CAN總線控制器晶片濾波器的作用、CAN設備的電源連接
    CAN是一種分布式的控制總線,總線上的每一個節點一般來說都比較簡單,使用MCU控制器處理CAN總線數據,完成特定的功能;通過CAN總線將各節點連接只需較少的線纜(兩根線:CAN_H和CAN_L),可靠性也較高。
  • JS事件對象Event詳解
    ,都會產生一個與之對應的事件對象 event ,其中包含了觸發事件的元素、鍵盤滑鼠的狀態、位置等等內容。event 對象代表事件的狀態,比如觸發事件的元素、鍵盤按鍵的狀態、滑鼠的位置、滑鼠按鍵的狀態等等;event 對象是一個隱式參數,並且只在事件發生的過程中才有效;event 對象根據觸發方式的不同會具有不同的屬性,也就是說某些屬性只對特定事件有效,但所有內容都是繼承自 Event 對象;event 對象在 IE 與 Chrome 等瀏覽器表現不盡相同
  • I2C總線要點總結
    說到總線,其種類很多,但其目的基本一致,就是一個用於傳遞信息的公共幹線。如晶片內部地址總線、數據總線其對象可能為CPU核與各外設(RAM/ROM/外設控制器電路等);晶片級總線I2C,SPI等,設備級總線如RS422/RS485/HART/CAN/Ethernet/Fieldbus.
  • 搞懂事件的使用,詳細解讀Solidity事件Event
    什麼是事件Evnet?就是以EVM日誌基礎設備提供一個接口,當被事件調用時,出發參數存儲到日誌中,其與合約地址關聯,並記錄到區塊鏈中。關係就是:區塊鏈是打包交易區塊組成的鏈條,每一個交易會包含0到多個記錄,日誌代表智能合約所觸發事件。
  • AMBA總線協議(三)——一文看懂AHB總線所有協議總結(AHB2 & AHB-Lite & AHB5 )
    AHB 5 協議:AMBA AHB協議總線的接口適用於高性能的設計。主模塊:Cortex-M內核,DMA存儲器,DMA外設,乙太網DMA USB等連接組件和從模塊:內部Flash,內部SRAM,AHB2APB橋,APB外設之間的接口。大多數掛在總線上的模塊,包括CPU只是單一屬性的功能模塊:主模塊或者從模塊。
  • 英語單詞,事件event,incident,accident辨識
    單詞構成event:e=ex外面;字面上的意思是「外面有事來了」有事發生了incident:in裡面,向內;字面上的意思是「有什麼東西掉落進來和event不一定,有可能壞事,也可能是好事。accident不用於政冶事件;incident說小事時就是日常不重要的事,說大事時多指在政冶軍事外交上可能導致爭端或戰爭的事件,(隱含的意思:因為這個事,會引發造成了某種附帶的後果)所以incident有個詞義「附帶的」。
  • Linux 4.4.220 PCI總線驅動分析
    然後往0xCF8-CFB寫入地址0x80000000,如果讀回來的還是0x80000000說明系統中有PCI總線,0xCF8-CDB並未被ISA總線使用。然後調用pci_sanity_check(),代碼如下圖
  • 「incident、accident、event」,都是「事件」,怎麼區分?
    incident&accident&event(1)accident一般用於出車禍和事故等非主觀性、突發性的事件,不可用於政治事件!(2)incident指日常突發的小插曲事件,不怎麼重要,比如說突然遇到好朋友。也可用於政治事件中,國際上的爭端用incident,因為會附帶的產生一些事的感覺。