【Vue.js】EventBus~親子関係にないコンポーネントのイベントに対してリアクティブな処理を行う~

Yudai Hirano

Yudai Hirano

Apr 09, 2021

背景

Vueでは、異なるコンポーネント間で通信するために

  1. event emitterを使って、親コンポーネントを介して2つの子コンポーネント間で通信する
  2. $refsで子コンポーネントと通信する

パターンが存在します。 しかし、これらの方法ではコンポーネント階層が深くなった場合

  1. evemt emitterの場合、コード量がおおくなる
  2. $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

;