背景
Vueでは、異なるコンポーネント間で通信するために
- event emitterを使って、親コンポーネントを介して2つの子コンポーネント間で通信する
- $refsで子コンポーネントと通信する
パターンが存在します。
しかし、これらの方法ではコンポーネント階層が深くなった場合
- evemt emitterの場合、コード量がおおくなる
- $refsの場合、子コンポーネント内でv-forしているコンポーネントに対してのアクセスが難しい
などの問題が発生します。
それを解決してくれるのがEventBusです。
EventBusとは、Vue.jsのインスタンスで、あるコンポーネントでイベントをemitし、emitされたイベントを別のコンポーネントで直接listenして反応することができます。中央または親コンポーネントを経由せずに、コンポーネント間の独立した通信を実現する安全な方法として機能します。一般的にpublish-subscribe approachと呼ばれる方法で実現されます。
事前条件
- 階層が深い関係にあるコンポーネント間で通信したい
処理
- EventBusを用いて、あるコンポーネントでイベントをemitし、emitされたイベントを別のコンポーネントで直接listenし反応させる
事後条件
- あるコンポーネントで発生したイベント時に別のコンポーネントで特定の処理を実行できる
ポイント
- EventBusのインスタンスを別のファイルとして作成し、データを共有する2つのコンポーネントにインポートする
- アプリがDOMにマウントされたときにリスニング処理を初期化するために、ライフサイクルフックを使う(created,mounted)
コード例
import Vue from 'vue'
export default new Vue()
<template>
<div class="left-side">
<div class="content">
<div>
<label for="location"> Location </label>
<input type="text" name="location" v-model="location" />
</div>
<div>
<label for="caption"> Caption </label>
<input type="text" name="caption" v-model="caption" />
</div>
<input type="submit" v-on:click="sendData" value="Send">
</div>
</div>
</template>
<script>
import EventBus from '../eventBus'
export default {
data () {
return {
location: '',
caption: ''
}
},
methods: {
sendData () {
const payload = {
location: this.location,
caption: this.caption
}
EventBus.$emit('DATA_PUBLISHED', payload)
}
}
}
</script>
<style scoped>
.left-side {
width: 50%;
height: 500px;
float: left;
background-color: #35495e;
}
input[type=submit] {
width: 100%;
background-color: #42b883;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
}
input[type=text], select {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
</style>
<template>
<div class="right-side">
<div class="content">
<p> {{ data.location }} </p>
<p> {{ data.caption }} </p>
</div>
</div>
</template>
<script>
import EventBus from '../eventBus'
export default {
data () {
return {
data: {
location: 'Location placeholder',
caption: 'Payload placeholder'
}
}
},
methods: {
updateData (payload) {
this.data = payload
}
},
mounted () {
EventBus.$on('DATA_PUBLISHED', (payload) => {
this.updateData(payload)
})
}
}
</script>
<style scoped>
.right-side {
width: 50%;
height: 500px;
float: right;
background-color: #42b883;
border: 1px;
border-color: black;
}
</style>
on()
メソッドは、EventBus上のイベントをリッスンするために使用されます。このメソッドは2つのパラメータを受け取ります。1つ目はイベント名、2つ目はコールバックメソッドです。
DATA_PUBLISHED
は、publisherが発行し、subscriber が購読するイベントを識別するためのイベント名です。
実は、この他にもoff()
とonce()
というメソッドがあります。off()
はsubscribeしたイベントを削除するのに使い、once()
はイベントをsubscribeするが一度しかトリガーされない場合に使います。購読者がトリガーされた後、購読は解除されます。詳細については、こちらの公式ドキュメントをご覧ください。
EventBusとしてのVueを使って行ったもう一つのクールなことは、サービスからのフェッチによってデータをリロードするために別のコンポーネントをトリガーすることです。データを更新するためにすべてのページをリロードする必要はなく、コンポーネント自体が行います。
参考・引用
https://blog.logrocket.com/using-event-bus-in-vue-js-to-pass-data-between-components/
https://medium.com/easyread/vue-as-event-bus-life-is-happier-7a04fe5231e1