Vue3中自定義事件總線的實現(xiàn)代碼
前言
在 Vue
開發(fā)中,組件之間的通信是一個常見的需求,無論是父組件向子組件傳遞數(shù)據(jù),還是子組件向父組件傳遞數(shù)據(jù),甚至是兄弟組件之間的數(shù)據(jù)交換。這些通信需求在構(gòu)建復(fù)雜的 Vue
應(yīng)用時尤為關(guān)鍵。
Vue
提供了多種組件通信的方式,如 props
用于父組件向子組件傳遞數(shù)據(jù),emit
用于子組件觸發(fā)事件并傳遞數(shù)據(jù)給父組件,vuex
適用于狀態(tài)管理場景,而 provide/inject
則提供了依賴注入的方式。在 Vue 2
中,還有 eventBus
和 $attrs/$listeners
以及 $parent/children
等方法來輔助組件間的通信。
然而,隨著 Vue 3
的發(fā)布,一些在 Vue 2
中常用的通信方式在 Vue 3
中可能不再適用或有所變化。為了應(yīng)對這種變化,我們可以選擇在 Vue 3
應(yīng)用中實現(xiàn)自定義的“事件總線”機制。這種機制通常是將一個事件中心(或稱為事件總線)掛載到Vue的全局對象上,從而使得任何組件都可以方便地通過事件總線來發(fā)布或監(jiān)聽事件。
通過使用這樣的自定義事件總線,開發(fā)者可以在 Vue 3
應(yīng)用中實現(xiàn)靈活的組件間通信,無論這些組件之間的層級關(guān)系如何,都能輕松地實現(xiàn)數(shù)據(jù)和事件的傳遞。
發(fā)布-訂閱模式
發(fā)布-訂閱模式(Publish-Subscribe Pattern)是一種在軟件設(shè)計中常見的模式,它允許消息發(fā)送者和接收者之間通過事件進(jìn)行通信,而不必直接相互依賴。這種模式的主要思想是通過一個被稱為“消息中心”或“事件總線”的實體來協(xié)調(diào)消息的發(fā)布和訂閱。
一個完整的發(fā)布-訂閱模式通常包含以下幾個部分:
- 發(fā)布者(Publisher):負(fù)責(zé)向消息中心發(fā)布事件或消息的對象。發(fā)布者通常不關(guān)心誰訂閱了這些事件,只負(fù)責(zé)在特定情況下觸發(fā)它們。
- 訂閱者(Subscriber):對特定事件感興趣的對象,它們會向消息中心訂閱這些事件。當(dāng)發(fā)布者發(fā)布一個事件時,所有訂閱了該事件的訂閱者都會收到通知。
- 消息中心(Event Bus/Message Center):負(fù)責(zé)管理事件的發(fā)布、訂閱和通知的對象。它存儲了事件和訂閱者之間的關(guān)系,并在事件被發(fā)布時,將事件通知給所有訂閱了該事件的訂閱者。
發(fā)布-訂閱模式的優(yōu)點包括:
- 解耦性:發(fā)布者和訂閱者之間不存在直接的依賴關(guān)系,這使得它們可以獨立地變化,而不需要修改對方。這種解耦性提高了代碼的可維護(hù)性和可擴展性。
- 可擴展性:可以輕松地添加新的發(fā)布者和訂閱者,而無需修改現(xiàn)有的代碼。這使得系統(tǒng)能夠靈活地適應(yīng)不斷變化的需求。
- 靈活性:支持多個訂閱者同時訂閱同一個事件,并且可以根據(jù)需要定制事件的處理方式。這種靈活性使得系統(tǒng)能夠應(yīng)對各種復(fù)雜場景。
- 時間解耦:發(fā)布者可以在任何時刻發(fā)布事件,而訂閱者則可以在自己方便的時候處理這些事件。這種時間解耦性使得系統(tǒng)更加靈活和高效。
綜上所述,發(fā)布-訂閱模式提供了一種強大而靈活的方式來處理組件之間的通信和協(xié)作,使得系統(tǒng)更加健壯、可維護(hù)和可擴展。在Vue.js
等現(xiàn)代前端框架中,發(fā)布-訂閱模式被廣泛應(yīng)用于組件之間的通信和狀態(tài)管理。
實現(xiàn)發(fā)布-訂閱模式
在深入探討發(fā)布-訂閱模式時,其核心機制在于一個精心構(gòu)建的事件中心。這個事件中心不僅作為消息的中轉(zhuǎn)站,還承載著存儲事件和訂閱者之間關(guān)系的重要職責(zé)。
事件中心
定義一個 EventBus
類,用于存儲事件和訂閱者關(guān)系,代碼如下:
interface EventType { readonly callback: Function readonly once: boolean } type EventsType = Record<string, EventType[]> class EventEmitter { private events: EventsType = {}; }
events
是一個對象,其中每個鍵都是事件名稱,值是一個由 EventType
對象組成的數(shù)組,EventType
對象中包含是否只訂閱一次標(biāo)志位和回調(diào)函數(shù)。
發(fā)布事件
定義一個 emit
方法,用于發(fā)布事件,代碼如下:
type EventArgs = Record<string, number | string | object | boolean> interface EventType { readonly callback: Function readonly once: boolean } type EventsType = Record<string, EventType[]> class EventEmitter { private events: EventsType = {} emit(eventName: string, eventArgs: EventArgs) { eventName?.split(',').forEach((eventKey: string) => { eventKey = eventKey.trim() const events = this.events[eventKey] || [] let { length } = events for (let i = 0; i < length; i++) { const { callback, once } = events[i] if (once) { events.splice(i, 1) if (length === 0) { delete this.events[eventKey] } length-- i-- } callback.apply(this, [eventArgs]) } }) } }
emit
方法接收兩個參數(shù),第一個參數(shù) eventName
為事件名稱,第二個參數(shù) eventArgs
為事件參數(shù)。其中 eventName
參數(shù)可以是一個以逗號分隔的字符串,表示同時發(fā)布多個事件。核心邏輯便是遍歷 events
對象,找到對應(yīng)的事件名稱,然后遍歷事件名稱對應(yīng)的事件數(shù)組,依次通過調(diào)用 apply
方法,執(zhí)行回調(diào)函數(shù)。
訂閱事件
定義一個 on
方法,用于訂閱事件,代碼如下:
class EventEmitter { private events: EventsType = {} on(eventName: string, callback: CallbackType, once?: boolean) { eventName?.split(',').forEach((eventKey: string) => { eventKey = eventKey.trim() if (!this.events[eventKey]) { this.events[eventKey] = []; } else { this.events[eventKey].push({ callback, once: !!once }) } }) return this } }
on
方法接收三個參數(shù),第一個參數(shù) eventName
為事件名稱,第二個參數(shù) callback
為回調(diào)函數(shù),第三個參數(shù) once
表示是否只訂閱一次。核心邏輯是遍歷 eventName
參數(shù) split
之后的數(shù)組對象(允許同時監(jiān)聽多個事件,多個事件之間以逗號分隔),將事件名稱拆分成數(shù)組,然后遍歷數(shù)組,將回調(diào)函數(shù)存入 events
對象中。
取消訂閱事件
定義一個 off
方法,用于取消訂閱事件,代碼如下:
class EventEmitter { private events: EventsType = {} off(eventName: string, callback?: CallbackType) { // 如果不傳callback,就清除所有事件 if (!eventName) { this.events = {} } eventName?.split(',').forEach((eventKey: string) => { if(!callback) { delete this.events[eventKey] } const events = this.events[eventKey] || [] let { length } = events for (let i = 0; i < length; i++) { if (events[i].callback === callback) { events.splice(i, 1) length -- i -- } } if (events.length === 0) { delete this.events[eventKey] } }) return this } }
off
方法接收兩個參數(shù),第一個參數(shù) eventName
為事件名稱,第二個參數(shù) callback
為回調(diào)函數(shù),核心邏輯是遍歷 eventName
參數(shù) split
之后的數(shù)組對象(允許同時取消訂閱多個事件,多個事件之間以逗號分隔),將事件名稱拆分成數(shù)組,然后遍歷數(shù)組,將回調(diào)函數(shù)從數(shù)組中刪除。
只訂閱一次
定義一個 once
方法,用于只訂閱一次,代碼如下:
class EventEmitter { private events: EventsType = {} once(eventName: string, callback: CallbackType) { return this.on(eventName, callback, true) } }
once
方法接收兩個參數(shù),第一個參數(shù) eventName
為事件名稱,第二個參數(shù) callback
為回調(diào)函數(shù)。once
方法內(nèi)部調(diào)用 on
方法,并將第三個參數(shù)設(shè)置為 true
,表示只訂閱一次。
如何在 Vue 中使用
當(dāng)我們想要在 Vue
應(yīng)用中使用發(fā)布-訂閱模式時,通常會引入一個全局的事件總線 (Event Bus)
來作為通信的中心。這樣,無論組件之間有著怎樣的層級關(guān)系,它們都可以輕松地通過事件總線進(jìn)行通信。
為了在 Vue
應(yīng)用中實現(xiàn)這一功能,我們需要在應(yīng)用的入口文件(通常是 main.ts
或 main.js
,取決于你的項目配置和所使用的 TypeScript
或 JavaScript
)中引入并實例化事件總線。然后,我們可以利用 Vue
的 provide
方法將事件總線注冊為全局對象,使得在 Vue
應(yīng)用的任何組件中都能通過 inject
來訪問它。
在 main.ts
中編寫代碼如下:
import { createApp } from 'vue' import { EventEmitter } from '@qftjs/tiny-editor-core' import App from './App.vue' const app = createApp(App) const bus = new EventEmitter() app.provide('$bus', bus) app.mount('#app')
在 Vue
組件中通過 inject
方法注入 $bus
對象,然后就可以使用 $bus
對象進(jìn)行事件訂閱和事件發(fā)布。具體代碼如下:
<script setup lang="ts"> import { inject } from 'vue' const $bus = inject('$bus') $bus.emit('dragStart') </script>
以上就是Vue3中自定義事件總線的實現(xiàn)代碼的詳細(xì)內(nèi)容,更多關(guān)于Vue3自定義事件總線的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺析Vue中defineProps的解構(gòu)和不解構(gòu)
defineProps?是?Vue?3?Composition?API?中用來聲明組件接收的?props?的方法,本文主要為大家介紹了defineProps的解構(gòu)和不解構(gòu),感興趣的可以了解下2025-02-02vue自定義插件封裝,實現(xiàn)簡易的elementUi的Message和MessageBox的示例
這篇文章主要介紹了vue自定義插件封裝,實現(xiàn)簡易的elementUi的Message和MessageBox的示例,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2020-11-11uniapp實現(xiàn)省市區(qū)三級級聯(lián)選擇功能(含地區(qū)json文件)
這篇文章主要給大家介紹了關(guān)于uniapp實現(xiàn)省市區(qū)三級級聯(lián)選擇功能(含地區(qū)json文件)的相關(guān)資料,級級聯(lián)是一種常見的網(wǎng)頁交互設(shè)計,用于省市區(qū)選擇,它的目的是方便用戶在一系列選項中進(jìn)行選擇,并且確保所選選項的正確性和完整性,需要的朋友可以參考下2024-06-06vue favicon設(shè)置以及動態(tài)修改favicon的方法
這篇文章主要介紹了vue favicon設(shè)置以及動態(tài)修改favicon的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12