Vue 组件间的通讯
這一節我們一起看看 vue 中組件間的數據是如何傳遞的。
前面,我們已經初步建立了 vue 組件化的思想,知道如何創建組件、引入組件以及如何在組件里的一些功能。接下來,我們來學習怎么建立組件之間的連接,也就是組件的通訊。直白一點說就是:在一個組件中做的操作如何更新到應用程序中的其他組件。
這篇文章會從兩個方便介紹 vue 組件間的通訊:
- 父子組件之間的通訊 - 兄弟組件之間的通訊 復制代碼一、父子組件之間的通訊
1、父組件向子組件通訊
在 vue 中,將數據從父組件傳遞到子組件,可以用 props 來實現(這一點,在 React 中也是如此)。
props 指的是從外部(父組件)設置的屬性。同時,為了告訴 vue 子組件需要從自已的外部(父組件)接收數據,需要在子組件的 vue 對象中設置 props 屬性。這個屬性是一個 String 數組,每個字符串表示一個可以從父組件接收的屬性。
我們需要做兩件事情:父組件使用屬性綁定、子組件使用 props 對象接收。 看個例子:
- 父組件使用屬性綁定 :
我們在父組件 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-linealert(this.home.list); // eslint-disable-linealert(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 的形式傳給子組件(借鑒 react 的父子通訊方式)
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-linealert(this.home.list); // eslint-disable-linealert(this.home.run); // eslint-disable-linealert(this.home.appendToList); // eslint-disable-line},// add thissubmit(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-linealert(this.home.list); // eslint-disable-linealert(this.home.run); // eslint-disable-linealert(this.home.appendToList); // eslint-disable-line},submit(text) {this.$emit('addItem', text);},},// add thisprops: ['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) ;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> 復制代碼打開瀏覽器,可以看到這樣也實現了兄弟組件之間的通訊。
三、全局模式
這里的全局模式指的是創建全局變量和全局方法,讓其他組件之間共享數據存儲的模式。我們看看怎么操作:
先創建一個 store.js ,在這個 JS 文件里創建全局的變量和方法:
const store = {state: { numbers: [1, 2, 3] },addNumber(newNumber) {this.state.numbers.push(newNumber);}, };export default store; 復制代碼在 store 的 state 中存放了一個 numbers 數組和一個 addNumber 方法。接下來我們創建兩個組件:
- NumberDisplay 組件:用來顯示來自 store 的 numbers 數組
- NumberSubmit 組件:允許用戶向數據數組中添加一個新的數字
創建 NumberDisplay 組件:
<template><div><h2>{{ storeState.numbers }}</h2></div> </template><script> import store from './store';export default {name: 'NumberDisplay',data() {return {storeState: store.state,};}, }; </script> 復制代碼創建 NumberSubmit 組件:
<template><div class='form'><input v-model='numberInput' type='number'><button @click='addNumber(numberInput)'>Add new number</button></div> </template><script> import store from './store';export default {name: 'NumberSubmit',data() {return {numberInput: 0,};},methods: {addNumber(numberInput) {store.addNumber(Number(numberInput));},}, }; </script> 復制代碼接著在 GlobalMode.vue 中引用剛才創建的組件:
<template><div><NumberDisplay/><NumberSubmit/></div> </template><script> import NumberDisplay from '../components/pass-data-3/NumberDisplay'; import NumberSubmit from '../components/pass-data-3/NumberSubmit';export default {name: 'GlobalMode',components: { NumberDisplay, NumberSubmit }, }; </script> 復制代碼效果如下:
可以看到,我們使用這種方式也可以實現組件間的通訊。
四、總結
最后,我們畫個圖總結一下 Vue 組件間的通訊:
本節內容代碼地址:github.com/IDeepspace/…
歡迎大家關注我的博客:togoblog.cn/
總結
以上是生活随笔為你收集整理的Vue 组件间的通讯的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google Chrome 增加拦截恶意
- 下一篇: Netty组件