Vue 元件間的通訊
這一節我們一起看看 vue
中元件間的資料是如何傳遞的。
前面,我們已經初步建立了 vue
元件化的思想,知道如何建立元件、引入元件以及如何在元件裡的一些功能。接下來,我們來學習怎麼建立元件之間的連線,也就是元件的通訊。直白一點說就是:在一個元件中做的操作如何更新到應用程式中的其他元件。
這篇文章會從兩個方便介紹 vue
元件間的通訊:
- 父子元件之間的通訊 - 兄弟元件之間的通訊 複製程式碼
一、父子元件之間的通訊
1、父元件向子元件通訊
在 vue
中,將資料從父元件傳遞到子元件,可以用 props
來實現(這一點,在 React
中也是如此)。
props
指的是從外部(父元件)設定的屬性。同時,為了告訴 vue
子元件需要從自已的外部(父元件)接收資料,需要在子元件的 vue
物件中設定 props
屬性。這個屬性是一個 String
陣列,每個字串表示一個可以從父元件接收的屬性。
我們需要做兩件事情:父元件使用屬性繫結、子元件使用 props 物件接收。看個例子:
- 父元件使用屬性繫結 :
<template> <div> <ChildCom :list='list' :run='run' :home='this'></ChildCom> </div> </template> <script> import ChildCom from './ChildCom'; export default { data() { return { list: ['我是父元件裡面的資料', '我來自父元件'], }; }, components: { ChildCom, }, methods: { run() { alert('我是父元件裡面的方法'); // eslint-disable-line }, }, }; </script> 複製程式碼
我們在父元件 ParentCom
裡面引入了子元件 ChildCom
。為了將資料從父元件傳到子元件,我們在子元件 ChildCom
上綁定了幾個屬性:
<childCom :list='list' :run='run' :home='this'></childCom> 複製程式碼
繫結屬性的時候,屬性名前需要加冒號。這裡我們綁定了三個屬性,父元件的 data
中的 list
、 methods
中的 run
方法以及指向父元件的 this
。
- 子元件使用 props 物件接收 :
接下來,我們建立一個 ChildCom
元件,通過子元件的 props
選項來獲得父元件傳過來的資料:
<template> <div> <div class='list'> <ul> <li v-for='item in list' :key='item'>{{ item }}</li> </ul> </div> <div class='buttons'> <button @click='run'>執行父元件的方法</button> <button @click='getParent()'>獲取父元件的資料和方法</button> </div> </div> </template> <script> export default { methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line }, }, props: ['list', 'run', 'home'], }; </script> <style lang="postcss" scoped> .list { margin-bottom: 10px; } li { margin: 10px 0; list-style: none; } button { padding: 6px; background-color: #35b880; border: none; color: white; font-size: 16px; margin: 5px; } </style> 複製程式碼
子元件的 props
中接收了父元件傳遞下來的屬性。 需要注意的是, props
字串陣列中的值( prop
)要和在父元件中為子元件繫結屬性的屬性名保持一致。
這裡我們加了一些樣式,在 App.vue
中引入父元件 ParentCom
,開啟瀏覽器會看到:

這樣,在子元件中就拿到了父元件傳遞下來的資料和方法以及父元件本身,點選按鈕就可以檢視到父元件傳遞給子元件的資料。
2、子元件向父元件通訊
前面我們知道了父元件如何向子元件通訊,那子元件如何向父元件通訊呢?這裡介紹兩種方式:
props
2.1 監聽事件
首先在子元件 ChildCom
的 <template>
中新增一個新的標籤 button
。在這個 button
上新增一個 click
事件:
<div class='buttons'> <!-- add this --> <button @click='submit("我是子元件傳遞給父元件的資料")'>子元件觸發更改父元件的資料</button> </div> 複製程式碼
當我們點選這個按鈕的時候,想要執行 submit
方法,我們在子元件的 <script>
中新增這個方法:
methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line alert(this.home.appendToList); // eslint-disable-line }, // add this submit(text) { this.$emit('addItem', text); }, }, 複製程式碼
觸發事件時發出( $emit
)自定義的事件: addItem
,這裡我們也給 addItem
事件傳遞了一個 text
引數。這樣就完成了 子元件發出自定義事件 的過程。
接下來需要在父元件中監聽子元件傳遞的自定義事件 addItem
。怎麼做呢?
在父元件中給子元件繫結監聽子元件中自定義的事件的方法。這就意味著我們也需要在父元件中定義這個方法。看看程式碼:
<template> <div> <ChildCom :list='list' :run='run' :home='this' @addItem='addItem'></ChildCom> </div> </template> <script> import ChildCom from './ChildCom'; export default { data() { return { list: ['我是父元件裡面的資料', '我來自父元件'], }; }, components: { ChildCom, }, methods: { run() { alert('我是父元件裡面的方法'); // eslint-disable-line }, addItem(item) { this.list.push(item); }, }, }; </script> 複製程式碼
在子元件上繫結監聽子元件中自定義事件的方法需要使用 @
符號,在 methods
中添加了 addItem
方法。這時候,我們開啟瀏覽器,點選第三個按鈕,就會看到子元件向父元件傳遞的資料了。

2.2 傳遞 props
傳遞 props
的意思是說在父元件裡面定義改變父元件資料的方法,然後通過 props
傳遞給子元件,這樣子元件就可以觸發執行從父元件傳遞下來的方法,達到更改父元件資料的目的。這種方法借鑑了 React
中元件通訊的方式。看看程式碼:
我們依舊使用上面的程式碼,在 ParentCom
元件中將 addItem
方法傳遞給子元件:
<ChildCom :list='list' :run='run' :home='this' @addItem='addItem' :addItem='addItem'></ChildCom> 複製程式碼
在子元件 ChildCom
中新增一個 button
,在它的點選事件中執行父元件的 addItem
方法,所以,我們也需要在子元件的 props
選項中把 addItem
方法新增進去:
<template> <div> <div class='list'> <ul> <li v-for='item in list' :key='item'>{{ item }}</li> </ul> </div> <div class='buttons'> <button @click='run'>執行父元件的方法</button> <button @click='getParent()'>獲取父元件的資料和方法</button> <button @click='submit("我是子元件傳遞給父元件的資料")'>子元件觸發更改父元件的資料</button> <!-- add this --> <button @click='addItem("我是通過子元件props方式傳遞給父元件的資料")'>子元件觸發更改父元件的資料-2</button> </div> </div> </template> <script> export default { data() { return {}; }, methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line alert(this.home.appendToList); // eslint-disable-line }, submit(text) { this.$emit('addItem', text); }, }, // add this props: ['list', 'run', 'home', 'addItem'], }; </script> 複製程式碼
開啟瀏覽器,點選 button
:

二、兄弟元件之間的通訊
在 vue
中實現兄弟元件間的通訊主要有兩種方法:**通過父元件進行兄弟元件之間通訊、通過 EventHub
進行兄弟元件間通訊。**為了不和上面講述的父子元件之間通訊的程式碼混淆,這裡我們重新新建元件來演示:
- 父元件:
ParentCard
- 兩個兄弟元件:
BrotherCard
和SisterCard
1、通過父元件進行兄弟元件之間通訊
簡單來說,就是讓兄弟元件通過一個共同父元件進行通訊。
首先建立父元件 ParentCard
:
<template> <div class='container'> <h2>父元件</h2> <button @click='stopCommunicate' v-if='showButton'>停止通訊</button> <div class='card-body'> <brother-card :messageSon='messageson' @brotherSaid='messageDaughter' class='card-brother'></brother-card> <sister-card :messageDaughter='messagedaughter' @sisterSaid='messageSon' class='card-sister'></sister-card> </div> </div> </template> <script> import BrotherCard from './BrotherCard'; import SisterCard from './SisterCard'; export default { name: 'ParentCard', data() { return { messagedaughter: '', messageson: '', }; }, components: { BrotherCard, SisterCard }, methods: { messageDaughter(message) { this.messagedaughter = message; }, messageSon(message) { this.messageson = message; }, showButton() { return this.messagedaughter && this.messageson; }, stopCommunicate() { this.messagedaughter = ''; this.messageson = ''; }, }, }; </script> <style scoped> .container { width: 70%; margin: 10px auto; border-radius: 10px; box-shadow: 1px 1px 1px 1px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07) !important; padding: 30px; } .card-body { display: flex; justify-content: center; } .card-brother, .card-sister { margin: 0 50px; } </style> 複製程式碼
建立 BrotherCard
元件:
<template> <div> <div> <p>我是子元件:Brother</p> <button @click='messageSister'>給妹妹發訊息</button> <div v-if='messageSon' v-html='messageSon'></div> </div> </div> </template> <script> export default { name: 'BrotherCard', props: ['messageSon'], methods: { messageSister() { this.$emit('brotherSaid', 'Hi,妹妹'); }, }, }; </script> 複製程式碼
建立 SisterCard
元件:
<template> <div> <div> <p>我是子元件:Sister</p> <button @click='messageBrother'>給哥哥發訊息</button> <div v-if='messageDaughter' v-html='messageDaughter'></div> </div> </div> </template> <script> export default { name: 'SisterCard', props: ['messageDaughter'], methods: { messageBrother() { this.$emit('sisterSaid', 'Hi,哥哥'); }, }, }; </script> 複製程式碼
結果如下:

在學習完父子元件之間的通訊方法之後,通過父元件進行兄弟元件的通訊就很簡單了,其實就是把兄弟之間需要共享的資料提升至他們最近的父元件當中進行管理,將他們的父元件作為中間媒介(在 React
中把這種方式被稱為狀態提升)。
2、通過EventHub進行兄弟間元件通訊
在 vue1.0
中,元件之間的通訊主要通過 $dispatch
沿著父鏈向上傳播和通過 $broadcast
向下廣播來實現。但是在 vue2.0
中 $dispatch
和 $broadcast
已經被棄用。
vue
中也提供了類似 Redux
的元件通訊和狀態管理方案: vuex
。對於中大型的專案來說,使用 vuex
是一個很好的選擇。但是對於小型的專案來說,如果一開始就引入了 vuex
,是完全沒必要的。
vue
官方文件中也給出了 $dispatch
和 $broadcast
最簡單的升級方式就是: 通過使用事件中心,允許元件自由交流,無論元件處於元件樹的哪一層。 vue
文件中把這個 事件中心 命名為 eventHub
,也有很多其他教程中將其命名為 eventBus
。在本教程中,我們統一命名為 eventHub
。
我們同樣基於上面的示例來做修改: ParentCard
元件包含了 SisterCard
和 BrotherCard
兩個子元件,而且這兩個子元件是兄弟元件。
首先在 main.js
檔案中定義一個新的 eventHub
物件( vue
例項 ):
import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; // add this export const eventHub = new Vue(); // eslint-disable-line /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>', }); 複製程式碼
接著我們要做的是讓 eventHub
例項成為 BrotherCard
元件中發出事件的例項,使用 eventHub.$emit
來替代上例中的 this.$emit
(因為 eventHub
是一個 vue
例項,所以它可以使用 $emit
方法)。
<template> <div> <p>我是Brother元件</p> <button @click='messageSister'>給妹妹發訊息</button> <div v-if='fromSister' v-html='fromSister'></div> </div> </template> <script> import { eventHub } from '../../main'; export default { name: 'BrotherCard', data: () => ({ fromSister: '', }), methods: { messageSister() { eventHub.$emit('brotherSaid', 'Hi,妹妹'); }, }, /* eslint-disable */ created() { eventHub.$on('sisterSaid', message => { this.fromSister = message; }); }, }; </script> 複製程式碼
引入 main.js
,並且將 created()
生命週期鉤子新增到 BrotherCard
元件中。在 created()
鉤子函式中新增 eventHub
啟動自定義事件的監聽器,監聽 sisterSaid
這個動作。
接下來我們改造下 SisterCard
元件,和 BrotherCard
元件的改造是一樣的:
<template> <div> <p>我是Sister元件</p> <button @click='messageBrother' class='btn'>給哥哥發訊息</button> <div v-if='fromBrother' v-html='fromBrother'></div> </div> </template> <script> import { eventHub } from '../../main'; export default { name: 'SisterCard', data: () => ({ fromBrother: '', }), methods: { messageBrother() { eventHub.$emit('sisterSaid', 'Hi,哥哥'); }, }, /* eslint-disable */ created() { eventHub.$on('brotherSaid', message => { this.fromBrother = message; }); }, }; </script> 複製程式碼
這時候,我們就不用在父元件 ParentCard
做任何操作了:
<template> <div class='container'> <h2>父元件</h2> <div class='card-body'> <brother-card class='card-brother'></brother-card> <sister-card class='card-sister'></sister-card> </div> </div> </template> <script> import BrotherCard from './BrotherCard'; import SisterCard from './SisterCard'; export default { name: 'ParentCard', components: { 'brother-card': BrotherCard, 'sister-card': SisterCard, }, }; </script> 複製程式碼
開啟瀏覽器,可以看到這樣也實現了兄弟元件之間的通訊。
三、總結
最後,我們畫個圖總結一下 Vue
元件間的通訊:

本節內容程式碼地址: github.com/IDeepspace/…
歡迎大家關注我的部落格:togoblog.cn/