vue篇之事件總線(EventBus)

許多現代JavaScript框架和庫的核心概念是可以將數據和UI封裝在模塊化、可重用的組件中。這對於開發人員能夠在開發整個應用程序時避免使用編寫大量重複的代碼。雖然這樣作很是有用,但也涉及到組件之間的數據通信。在Vue中一樣有這樣的概念存在。經過前面一段時間的學習,Vue組件數據通信經常會有父子組件,兄弟組件之間的數據通信。也就是說在Vue中組件通信有必定的原則。前端

父子組件通信原則

爲了提升組件的獨立性與重用性,父組件會經過 props 向下傳數據給子組件,當子組件有事情要告訴父組件時會經過 $emit 事件告訴父組件。如此確保每一個組件都是獨立在相對隔離的環境中運行,能夠大幅提升組件的維護性。vue

在《Vue組件通信》一文中有詳細介紹過這部分。但這套通信原則對於兄弟組件之間的數據通信就有必定的詬病。固然,在Vue中有其餘的方式來處理兄弟組件之間的數據通信,好比Vuex這樣的庫。但在不少狀況之下,我們的應用程序不須要相似Vuex這樣的庫來處理組件之間的數據通信,而能夠考慮Vue中的 事件總線 ,即 **EventBus **。設計模式

接下來的內容,就是來一塊兒學習Vue中的 EventBus 相關的知識點。bash

EventBus的簡介

EventBus 又稱爲事件總線。在Vue中可使用 EventBus 來做爲溝通橋樑的概念,就像是全部組件共用相同的事件中心,能夠向該中心註冊發送事件或接收事件,因此組件均可以上下平行地通知其餘組件,但也就是太方便因此若使用不慎,就會形成難以維護的災難,所以才須要更完善的Vuex做爲狀態管理中心,將通知的概念上升到共享狀態層次。app

如何使用EventBus

在Vue的項目中怎麼使用 EventBus 來實現組件之間的數據通信呢?具體能夠經過下面幾個步驟來完成。框架

初始化

首先你須要作的是建立事件總線並將其導出,以便其它模塊可使用或者監聽它。咱們能夠經過兩種方式來處理。先來看第一種,新建立一個 .js 文件,好比 event-bus.js :模塊化

// event-bus.js


import Vue from 'vue'
export const EventBus = new Vue()
複製代碼

你須要作的只是引入 Vue 並導出它的一個實例(在這種狀況下,我稱它爲 EventBus )。實質上它是一個不具有 DOM 的組件,它具備的僅僅只是它實例方法而已,所以它很是的輕便。學習

另一種方式,能夠直接在項目中的 main.js 初始化 EventBus :ui

// main.js
Vue.prototype.$EventBus = new Vue()
複製代碼

注意,這種方式初始化的 EventBus 是一個 全局的事件總線 。稍後咱們會花點時間專門聊一聊全局的事件總線。this

如今咱們已經建立了 EventBus ,接下來你須要作到的就是在你的組件中加載它,而且調用同一個方法,就如你在父子組件中互相傳遞消息同樣。

發送事件

假設你有兩個子組件: DecreaseCount 和 IncrementCount ,分別在按鈕中綁定了 decrease()和 increment() 方法。這兩個方法作的事情很簡單,就是數值遞減(增) 1 ,以及角度值遞減(增) 180 。在這兩個方法中,經過 EventBus.$emit(channel: string, callback(payload1,…)) 監聽 decreased (和 incremented )頻道。

<!-- DecreaseCount.vue -->
<template>
    <button @click="decrease()">-</button>
</template>

<script> import { EventBus } from "../event-bus.js";
    export default {
        name: "DecreaseCount",
        data() {
            return {
                num: 1,
                deg:180
            };
        },
        methods: {
            decrease() {
                EventBus.$emit("decreased", {
                    num:this.num,
                    deg:this.deg
                });
            }
        }
    }; 
</script>

<!-- IncrementCount.vue -->
<template>
    <button @click="increment()">+</button>
</template>

<script> import { EventBus } from "../event-bus.js";
    export default {
        name: "IncrementCount",
        data() {
            return {
                num: 1,
                deg:180
            };
        },
        methods: {
            increment() {
                EventBus.$emit("incremented", {
                    num:this.num,
                    deg:this.deg
                });
            }
        }
    };
 </script>
複製代碼

上面的示例,在 DecreaseCount 和 IncrementCount 分別發送出了 decreased 和 incremented頻道。接下來,咱們須要在另外一個組件中接收這兩個事件,保持數據在各組件之間的通信。

接收事件

如今咱們能夠在組件 App.vue 中使用 EventBus.$on(channel: string, callback(payload1,…))監聽 DecreaseCount 和 IncrementCount 分別發送出了 decreased 和 incremented 頻道。

<!-- App.vue -->
<template>
    <div id="app">
        <div class="container" :style="{transform: 'rotateY(' + degValue + 'deg)'}">
            <div class="front">
                <div class="increment">
                    <IncrementCount />
                </div>
                <div class="show-front"> {{fontCount}} </div>
                <div class="decrement">
                    <DecreaseCount />
                </div>
            </div>

            <div class="back">
                <div class="increment">
                    <IncrementCount />
                </div>
                <div class="show-back"> {{backCount}} </div>
                <div class="decrement">
                    <DecreaseCount />
                </div>
            </div> 
        </div>
    </div>
</template>

<script>
    import IncrementCount from "./components/IncrementCount";
    import DecreaseCount from "./components/DecreaseCount";
    import { EventBus } from "./event-bus.js";
    export default {
        name: "App",
        components: {
            IncrementCount,
            DecreaseCount
        },
        data() {
            return {
                degValue:0,
                fontCount:0,
                backCount:0
            };
        },
        mounted() {
            EventBus.$on("incremented", ({num,deg}) => {
                this.fontCount += num
                this.$nextTick(()=>{
                    this.backCount += num
                    this.degValue += deg;
                })
            });
            EventBus.$on("decreased", ({num,deg}) => {
                this.fontCount -= num
                this.$nextTick(()=>{
                    this.backCount -= num
                    this.degValue -= deg;
                })
            });
        }
    }; 
</script>
複製代碼

最終獲得的效果以下:

最後用一張圖來描述示例中用到的 EventBus 之間的關係:

若是你只想監聽一次事件的發生,可使用 EventBus.$once(channel: string, callback(payload1,…)) 。

移除事件監聽者

若是想移除事件的監聽,能夠像下面這樣操做:

import { eventBus } from './event-bus.js'
EventBus.$off('decreased', {})
複製代碼

你也可使用 EventBus.$off(‘decreased’) 來移除應用內全部對此事件的監聽。或者直接調用EventBus.$off() 來移除全部事件頻道, 注意不須要添加任何參數 。

上面就是 EventBus 的使用方式,是否是很簡單。上面的示例中咱們也看到了,每次使用 EventBus 時都須要在各組件中引入 event-bus.js 。事實上,咱們還能夠經過別的方式,讓事情變得簡單一些。那就是建立一個全局的 EventBus 。接下來的示例向你們演示如何在Vue項目中建立一個全局的 EventBus 。

全局EventBus

全局EventBus,雖然在某些示例中不提倡使用,但它是一種很是漂亮且簡單的方法,能夠跨組件之間共享數據。

它的工做原理是發佈/訂閱方法,一般稱爲 Pub/Sub 。

這整個方法能夠看做是一種設計模式,由於若是你查看它周圍的東西,你會發現它更像是一種體系結構解決方案。咱們將使用普通的JavaScript,並建立兩個組件,並演示EventBus的工做方式。

讓咱們看看下圖,並試着瞭解在這種狀況下究竟發生了什麼。

咱們從上圖中能夠得出如下幾點:

  • 有一個全局EventBus
  • 全部事件都訂閱它
  • 全部組件也發佈到它,訂閱組件得到更新
  • 總結一下。全部組件都可以將事件發佈到總線,而後總線由另外一個組件訂閱,而後訂閱它的組件將獲得更新

在代碼中,咱們將保持它很是小巧和簡潔。咱們將它分爲兩部分,將展現兩個組件以及生成事件總線的代碼。

建立全局EventBus

全局事件總線只不過是一個簡單的 vue 組件。代碼以下:

var EventBus = new Vue();

Object.defineProperties(Vue.prototype, {
    $bus: {
        get: function () {
            return EventBus
        }
    }
})
複製代碼

如今,這個特定的總線使用兩個方法 $on 和 $emit 。一個用於建立發出的事件,它就是$emit ;另外一個用於訂閱 $on :

var EventBus = new Vue();

this.$bus.$emit('nameOfEvent',{ ... pass some event data ...});

this.$bus.$on('nameOfEvent',($event) => {
    // ...
})
複製代碼

如今,咱們建立兩個簡單的組件,以便最終得出結論。

接下來的這個示例中,咱們建立了一個 ShowMessage 的組件用來顯示信息,另外建立一個 UpdateMessage 的組件,用來更新信息。

在 UpdateMessage 組件中觸發須要的事件。在這個示例中,將觸發一個 updateMessage 事件,這個事件發送了 updateMessage 的頻道:

<!-- UpdateMessage.vue -->
<template>
    <div class="form">
        <div class="form-control">
            <input v-model="message" >
            <button @click="updateMessage()">更新消息</button>
        </div>
    </div>
</template>
<script>
export default {
        name: "UpdateMessage",
        data() {
            return {
                message: "這是一條消息"
            };
        },
        methods: {
            updateMessage() {
                this.$bus.$emit("updateMessage", this.message);
            }
        },
        beforeDestroy () {
            $this.$bus.$off('updateMessage')
        }
    };
 </script>
複製代碼

同時在 ShowMessage 組件中監聽該事件:

<!-- ShowMessage.vue -->
<template>
    <div class="message">
        <h1>{{ message }}</h1>
    </div>
</template>

<script> 
export default {
        name: "ShowMessage",
        data() {
            return {
                message: "我是一條消息"
            };
        },
        created() {
            var self = this
            this.$bus.$on('updateMessage', function(value) {
                self.updateMessage(value);
            })
        },
        methods: {
            updateMessage(value) {
                this.message = value
            }
        }
    }; 
</script><
複製代碼

最終的效果以下:

從上面的代碼中,咱們能夠看到 ShowMessage 組件偵聽一個名爲 updateMessage 的特定事件,這個事件在組件實例化時被觸發,或者你能夠在建立組件時觸發。另外一方面,咱們有另外一個組件UpdateMessage ,它有一個按鈕,當有人點擊它時會發出一個事件。這致使訂閱組件偵聽發出的事件。這產生了 Pub/Sub 模型,該模型在兄弟姐妹之間持續存在而且很是容易實現。

總結

本文主要經過兩個實例學習了Vue中有關於 EventBus 相關的知識點。主要涉及了 EventBus 如何實例化,又是怎麼經過 $emit 發送頻道信號,又是如何經過 $on 來接收頻道信號。最後簡單介紹了怎麼建立全局的 EventBus 。從實例中咱們能夠了解到, EventBus 能夠較好的實現兄弟組件之間的數據通信。 對前端技術感興趣的同窗,歡迎加入Q羣:866109386,無論你是小白仍是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時天天更新視頻文件資料。 最後,祝你們早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峯。

相關文章
相關標籤/搜索