這一節咱們一塊兒看看 vue
中組件間的數據是如何傳遞的。javascript
前面,咱們已經初步創建了 vue
組件化的思想,知道如何建立組件、引入組件以及如何在組件裏的一些功能。接下來,咱們來學習怎麼創建組件之間的鏈接,也就是組件的通信。直白一點說就是:在一個組件中作的操做如何更新到應用程序中的其餘組件。css
這篇文章會從兩個方便介紹 vue
組件間的通信:html
- 父子組件之間的通信 - 兄弟組件之間的通信
<!-- more -->vue
在 vue
中,將數據從父組件傳遞到子組件,能夠用 props
來實現(這一點,在 React
中也是如此)。java
props
指的是從外部(父組件)設置的屬性。同時,爲了告訴 vue
子組件須要從自已的外部(父組件)接收數據,須要在子組件的 vue
對象中設置 props
屬性。這個屬性是一個 String
數組,每一個字符串表示一個能夠從父組件接收的屬性。react
咱們須要作兩件事情:父組件使用屬性綁定、子組件使用 props 對象接收。 看個例子:git
<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
上綁定了幾個屬性:github
<childCom :list='list' :run='run' :home='this'></childCom>
綁定屬性的時候,屬性名前須要加冒號。這裏咱們綁定了三個屬性,父組件的 data
中的 list
、methods
中的 run
方法以及指向父組件的 this
。vuex
接下來,咱們建立一個 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
,打開瀏覽器會看到:
這樣,在子組件中就拿到了父組件傳遞下來的數據和方法以及父組件自己,點擊按鈕就能夠查看到父組件傳遞給子組件的數據。
前面咱們知道了父組件如何向子組件通信,那子組件如何向父組件通信呢?這裏介紹兩種方式:
props
的形式傳給子組件(借鑑 react 的父子通信方式)首先在子組件 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
方法。這時候,咱們打開瀏覽器,點擊第三個按鈕,就會看到子組件向父組件傳遞的數據了。
傳遞 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
簡單來講,就是讓兄弟組件經過一個共同父組件進行通信。
首先建立父組件 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
中把這種方式被稱爲狀態提高)。
在 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
組件間的通信:
本節內容代碼地址: https://github.com/IDeepspace...
歡迎關注個人博客: https://togoblog.cn/