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

## 背景
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)

## コード例
```js:eventBus.js
import Vue from 'vue'
export default new Vue()
```
```vuejs:Leftside.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>
```
```vuejs:Rightside.vue
<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するが一度しかトリガーされない場合に使います。購読者がトリガーされた後、購読は解除されます。詳細については、[こちら](https://vuejs.org/v2/api/#Instance-Methods-Events)の公式ドキュメントをご覧ください。
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