兄弟組件之間的通信同樣是在項目中經常會遇到的組件間的通信問題之一, 這種問題的最根本方法就是: 把兄弟組件內部的變量提升到一箇中央倉庫。
藉助父級組件鏈式交互
使子組件1 通過 $emit
通知父級, 父級再通過響應 子組件1 的事件去觸發子組件2的事件,這樣的鏈式操作,在子組件不多的時候,但是一個不錯的解決方法
子組件1
<template>
<div>
<p @click="$emit('fromFirst','來自A組件')">first組件</p>
</div>
</template>
<script>
export default {
name: 'first'
}
</script>
子組件2
<template>
子組件2
<div>{{secondInfo}}</div>
</template>
<script>
export default {
name: 'second',
data() {
return {
this.secondInfo: null
}
},
created(){
this.$on('fromFather', (info) => {
this.secondInfo = info
})
}
}
</script>
父組件
<template>
<first @fromFirst='handleFromFirst' />
<second ref='second' />
</template>
<script>
import First from './first'
import Scond from './second'
export default {
components: {First, Second},
data() {
return {
this.secondInfo: null
}
},
methods:{
handleFromFirst(val) {
let second = this.$refs.second
second.$emit('fromFather', val)
}
}
}
</script>
子組件1 觸發父組件的 fromFirst
事件, 在事件中又觸發了子組件2的 fromFather
事件,並將從子組件1 傳遞過來的參數傳遞給了該事件, 當子組件2 執行該事件的時候,將內部的 secondInfo
改變。這就實現了一個兄弟組件的交互。
這個方式在 react
裏面同樣也是適用的, 但是如果父組件內包含了多個子組件幷包含了複雜的邏輯, 有沒有更好的方式來解決這種方式呢。
大部分第一個想到的是 vuex
, 當然這在一個業務邏輯、數據複雜的項目中是一個很好的解決方法, 但是想象我們要編寫一個通用組件,這個組件可能被用到不同的項目中來, 如果使用 vuex
這就要求每一個使用這個組件的項目中都要使用 vuex
, 這顯然是不好的。
藉助中間文件,充當中央倉庫
還好 ES6
的模塊機制天然就支持建立一箇中央倉庫, 當 A 文件使用 import value from './b.js'
來引用 B 文件裏面的 value
的時候, 這時就會賦值給 A 文件一個 B 文件的 value
的 只讀引用
, 當 B 文件裏面的 value
的值發生變化的時候, A 文件裏面的 value
也會跟着改變。
// lib.js
export let counter = 3;
export function incCounter() {
counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
定義一個額外的實例進行一個事件的中轉,對於ES6 模塊的運行機制已經有了一個講解,當模塊內部發生變化的時候,引入模塊的部分同樣會發生變化,當又一個額外的實例對加載機制進行引入進行on進行綁定通信,能輕而易舉解決問題,通過b->a->c的模式直接過渡。
解決 vue 兄弟組件之間的通信我們同樣也可以使用中央倉庫的方式來實現。
// store.js 作爲中央倉庫
import Vue from 'vue'
export default new Vue()
通過 new 一個 vue 的實例當作兄弟組件交互的中央倉庫。
父級組件
<template>
<first/>
<second/>
</template>
<script>
import First from './first'
import Scond from './second'
export default {
components: {First, Second}
}
</script>
父組件只是引入子組件, 不再作爲中央倉庫來過渡交互。
子組件1
<template>
<div @click='hanleClick'>子組件1</div>
</template>
<script>
import Store from './store'
export default {
name: 'first',
methods: {
handleClick() {
Store.$emit('fromFirst', '來自子組件1的傳值')
}
}
}
</script>
因爲我們的目的就是把 Store
作爲一箇中央倉庫,這裏我們把 fromFirst
事件添加到了 Store
上面而不是當前組件 this
上。
子組件2
<template>
子組件2
<div>{{secondInfo}}</div>
</template>
<script>
import Store from './store'
export default {
name: 'second',
data() {
return {
this.secondInfo: null
}
},
created(){
Store.$on('fromFirst', (info) => {
this.secondInfo = info
})
}
}
</script>
子組件2 裏面同樣也是使用 Store
實例來監聽 fromFirst
事件, 因爲子組件1和子組件2裏面添加事件和監聽事件的是同一個實例,根據我們在上文中分析的 ES6
中的情況, 當 Store
添加了 fromFirst
這個時間之後, Store
實例的 $on
就可以監聽到這個事件並執行回調。
跨組件深層次交互
上面講的組件之間的關係是這樣的:
我們可以實現 子組件之間的交互, 但是如果我們遇到這種情況呢?
孫組件需要跟子組件3 進行交互,還是使用上述的方法可以做到嗎? 答案是肯定的,只要能夠使用同一個中央倉庫,那麼不管什麼層級的組件複雜度,都是可以實現兩者的交互的。