使用事件匯流排共享元件之間的Props
特別宣告,本文根據 ofollow,noindex" target="_blank">@KINGSLEY SILAS 的《 Using Event Bus to Share Props Between Vue Components 》一文所整理。
預設情況下,Vue元件之間的通訊是通過 Props
來完成的。 Props
是從父元件向子元件傳遞屬性。例如,這裡有一個元件, title
是一個 prop
:
<blog-post title="My journey with Vue"></blog-post>
Props
總是從父元件向子元件傳遞。隨著應用程式複雜度的增加,你會慢慢遇到所謂的 Prop Drilling ,這裡有 一篇文章 介紹了這方面的東西,雖然是React方面的,但也適用於Vue。Prop Drilling是將 props
向下、向下、向下傳遞給子元件的想法 —— 正如你想象的那樣,這通常是一個乏味的過程。
因此,繁瑣的Prop Drilling可能是一個複雜的潛在問題。另一個與不相關的元件之間的通訊有關。我們可以通過使用 事件匯流排 來解決這些問題。
什麼是事件匯流排?這個名字本身就是一個總結。這是一個元件將 props
從一個元件傳遞到另一個元件的一種運輸方式,無論這些元件伴於樹的哪個位置。
練習任務:建立一個計數器
讓我們一起構建一些東西,用來幫助我們演示事件匯流排相關的概念。一個計數器,增加或減少 input
中輸入的數值,並記錄總數。
為了使用事件匯流排,我們首先需要對事件匯流排進行初始化。可以在 src
下建立一個 event-bus.js
檔案,新增下面這段程式碼,對事件匯流排進行初始化:
// event-bus.js import Vue from 'vue' export const EventBus = new Vue()
除了像上面那樣對事件進行初始化之外,還可以對事件匯流排進行全域性初始化。有關於這方面的詳細介紹,可以閱讀前段時間整理的一篇文章《 Vue 2.0學習筆記:事件匯流排(EventBus) 》。
這裡將 Vue
例項設定為 EventBus
。當然,你也可以隨便給它起一個你喜歡的名字。如果你使用的是一個 單檔案元件 ,那麼你應該在一個單獨的檔案中新增下面這段程式碼,因為無論如何你都必須匯出( export
)分配給 EventBus
的 Vue
例項。
import Vue from 'vue'; export const eventBus = new Vue();
這樣我們就可以在計數器元件( Counter
)中使用它了。不過在上面的示例中,為 EventBus
初始化單獨建立了一個 event-bus.js
,在這個檔案中添加了初始化 EventBus
的程式碼。在 Counter
元件可以像下面這樣呼叫已初始化的 EventBus
:
<!-- Counter.vue --> import { EventBus } from "../event-bus.js";
接下一我們要做的是:
- 我們想要一個初始值為
0
的count
- 我們需要一個接受數值的輸入框
input
- 我們需要兩個按鈕
button
:一個在單擊時將提交的數值新增到計數(count
)中,另一個將在單擊時從計數中減去提交的數值 - 我們想確認當計數改變時發生了什麼
我們在 Counter
中的模板可以這樣寫:
<!-- Counter.vue --> <template> <div class="counter"> <h3>{{ count }}</h3> <div :class="$style.form"> <button @click.prevent="handleIncrement" :class="[$style.btn, $style['btn-increment']]">+</button> <input type="number" v-model="entry" :class="$style.input" /> <button @click.prevent="handleDecrement" :class="[$style.btn, $style['btn-decrement']]">-</button> </div> <div class="form-help">{{ text }}</div> </div> </template>
通過 v-model
將 entry
的值繫結到 input
,根據使用者輸入的內容,我們將使用該值來增加或減少計數。當單擊任何一個按鈕時,都會觸發一個方法,該方法會增加或減少 count
的值。最後,在 <p>
標籤中包含的 {{ text }}
會打印出提示訊息(當 count
值更改後)。
以下是 Counter
元件對應的指令碼程式碼:
<!-- Counter.vue --> <script> import { EventBus } from "../event-bus.js"; export default { name: "Counter", data() { return { count: 0, entry: 3, text: '' }; }, created () { EventBus.$on('count-incremented', () => { this.text = `Count was increased` setTimeout(() => { this.text = '' }, 3000) }) EventBus.$on('count-decremented', () => { this.text = `Count was decreased` setTimeout(() => { this.text = '' }, 3000) }) }, methods: { handleIncrement () { this.count += parseInt(this.entry, 10) EventBus.$emit('count-incremented') this.entry = 0 }, handleDecrement () { this.count -= parseInt(this.entry, 10) EventBus.$emit('count-decremented') this.entry = 0 } } }; </script>
我們要做的第一件事是建立一個從一個元件傳送事件到另一個元件的路徑。我們可以使用 EventBus.$emit()
(通過 $emit()
)來鋪路。這種傳送包含在兩個方法中, handleIncrement
和 handleDecrement
,這兩個方法用來監聽 input
的提交。一旦事件發生,我們的事件匯流排就會向任何請求資料的繃件傳送 Props
。
你可能已經注意到,我們正在使用 EventBus.$on()
在 created()
鉤子中偵聽這兩個事件。在這兩個事件中,我們必須傳遞與我們發出的事件相對應的字串。這類似於特定事件的識別符號,以及為元件建立接收資料方式的識別符號。當 EventBus
識別已宣佈的特定事件時,接下來的函式將被呼叫 —— 我們設定一個文字來顯示發生了什麼,並在三秒鐘後使其消失。
練習任務:處理多個元件
假設我們正在開發一個配置檔案頁面,使用者可以在該頁面上更新自己的名字和電子郵件地址,然後在不重新整理頁面的情況下檢視更新。即使我們要處理兩個元件,也可以使用事件匯流排來實現這個效果。
在上面的這個示例中,我們建立了 ShowProfile
和 EditProfile
兩個元件,前者用來顯示個人資訊,後者用來編輯個人資訊。兩個元件的 <template>
分別如下:
<!-- ShowProfile.vue --> <template> <div :class="$style.profile"> <h1>{{ name }}</h1> <p>{{ email }}</p> </div> </template> <!-- EditProfile.vue --> <template> <div :class="$style['edit-profile']"> <h3>Enter your details below</h3> <form @submit.prevent="handleSubmit"> <div :class="$style['form-controle']"> <label for="name">Name</label> <input type="text" v-model="user.name" id="name" /> </div> <div :class="$style['form-controle']"> <label for="email">Email</label> <input type="email" v-model="user.email" id="email" /> </div> <div :class="$style['form-controle']"> <button>Submit</button> </div> </form> </div> </template>
我們把 ids
( user.name
和 user.email
)傳遞相應的元件。首先,讓我們為 EditProfile
元件設定模板,它包含我們想要傳遞給 ShowProfile
元件的 name
和 email
資料。同樣,我們也和前面的示例一樣,建立了一個事件匯流排,在它監聽到 submit
事件後發出該資料。
<!-- EditProfile.vue --> <script> import { EventBus } from "../event-bus.js"; export default { name: "EditProfile", data() { return { user: { name: "", email: "" } }; }, methods: { handleSubmit() { EventBus.$emit("form-submitted", this.user); this.user = {}; } } }; </script>
在 ShowProfile
元件中的 data
主要用於使用者提交資料時, ShowProfile
中的 data
進行反應性(Reactively)更新,該元件查詢事件匯流排到達其集線器時輸入的 name
和 email
。
<!-- ShowProfile.vue --> <script> import { EventBus } from "../event-bus.js"; export default { name: "ShowProfile", data() { return { name: "W3cplus", email: "[email protected]" }; }, created() { EventBus.$on("form-submitted", ({ name, email }) => { this.name = name; this.email = email; }); } }; </script>
很酷,對吧!儘管 ShowProfile
和 EditProfile
元件是兄弟關係(不是直接的父子關係),但是它們可以通過相同的事件進行資料通訊。
總結
事件匯流排想在應用程式中啟用反應的情況下是很有幫助的。具體來說,就是從伺服器獲得嚮應更新元件,而不會導致頁面重新整理。還可能有多個元件可以偵聽發出的事件。如果你有其他關於使用事件匯流排的有趣場景,歡迎在下面的評論中與我們共享。