GAミント至上主義

Web Monomaniacal Developer.

Vue.js v2で親子間以外のイベントやり取り

最近はずっとVue.jsを使ってUIを開発しているけど、ページが複雑になるにつれ、どうしても親子間以外の兄弟とか、深くなったコンポーネントからとか、グローバルなイベントのやり取りが必要になってきた。

発信側はthis.$emit(’foobar’, data)と、受信するコンポーネントでv-on:foobar="bazzMethod"を書いて繋げていくのも不可能ではないけど、メンテナンスを考えると不可能。

ずっと検索でさまよっていたところ、公式ドキュメントでv1.xからの移行にあった。昔は$broadcastと$dispatchというのがあったらしい。
jp.vuejs.org

2ではeventHubという概念を使うらしい。Vueの機能とは言えない気がするので概念とした。

一つグローバルなVueオブジェクトを作って、みんなそれからemitしたり、onしたりするだけ。

公式のドキュメントだとvue-routerを使う場合はつらみがあるので、Vueオブジェクトのprototypeにぶち込んでしまい、どのコンポーネントからでも呼び出せる下記のやり方が非常に便利。

Create a global event bus in Vue.js – VueJobs – Medium

このやり方でもいいんだけど、webpackのテンプレートを使っていて、main.jsをなるべく汚したくないし、せっかくなので別の機能でプラグインの使い方も覚えたので、プラグイン形式で作り直すことにした。

まずプラグイン用ファイルを作る

EventHub.js

const eventHub = {
  install: function (Vue, options) {
    Vue.prototype.$eventHub = new Vue()
  }
}
export default eventHub


あとは呼び出して登録するだけ

main.jsとか(このテンプレートを使った場合)に追加

import EventHub from './path/to/EventHub'
Vue.use(EventHub)

これで、コンポーネント内からthis.$eventHubで呼び出せるようになる。

on側はだいたいコンポーネントのmountedでいい気がしている

  mounted: function () {
    this.$eventHub.$on('save-complete-notify', this.fooMethod)
 }

emit側はmethodsとか、watchとかでやる

sendMessage function () {
      this.$eventHub.$emit('save-complete-notify', '保存したよ')
}

テンプレートだとthisはいらない

<button @click="$eventHub.$emit('download', data)">ダウンロード</ebutton>

一度eventHubを登録してしまえば、mountedでonすればどこからでもemitして処理ができるのでめっちゃ楽になった。

グローバル登録してしまうのでアプリケーションの規模や開発人数によっては名前がかぶってしまうとか、つらくなってしまうかもしれないけど、Vueで大人数で開発するのは何か間違ってる気がするので問題になることは少なそう。

$emitと$onしたいというより、dataをグローバルで保持したいということになってきたら公式の状態管理ライブラリであるVuexの出番だと思うけど、今の所出番はない。

Introduction · Vuex