Vueでグローバルにイベントを扱うためにプラグインを作ってみた

Vueでグローバルにイベントを扱うためにプラグインを作ってみた

こんにちは今年の2月に入社しましたs4it0です。今回はVueネタを一つ書いてみたいと思います。

Vueである程度大きなものを作り始めるとコンポーネントがネストしてイベントをバケツリレーして親の親の…みたいなことになることもあります。…ありますよね?

色々調べてみると公式ドキュメントのちょっとわかりにくいところにこういう場合の解決策が書いてあります。

Vue 1.x からの移行 — Vue.js

公式ではeventHubと書いてありますが色々調べるとeventBusと呼んでいるところもありました。

ここでは語感が好きというだけの理由でeventBusとして書いていきたいと思います。

eventBusとは

考え方は難しくなく、単に空のVueのインスタンスを経由してイベントをやり取りします。

eventBusを使わない場合

従来だとこんなかんじ。孫くらいならまだ我慢できますがもっとコンポーネントのネストが深くなるとイベント管理が煩雑になっていき保守性も悪化していきます。

eventBusを使った場合

eventBusを使うとこんな感じのイメージになります。
別のVueインスタンスを経由してイベントのやりとりをするのでどのコンポーネントからでもイベントのやり取りができます。

サンプルコードはドキュメントにまとまっているのでそちらを見るのがわかりやすいですが、ドキュメントのままだと使いたいところでインポートする必要があります。

せっかくならプラグイン化してしまってより使いやすくしてみましょう。

プラグインの作成

Vueの基本的なプラグインの作り方はそこまで難しくありません。以下のドキュメントに基本的な作り方の記載があります。

プラグイン — Vue.js

プラグインは最低限 install メソッドを公開している必要があり、第 1 引数はVueコンストラクタ、第 2 引数は任意でoptionsが指定されて呼び出されます。公式ドキュメントにあるサンプルコードは以下の通り。

MyPlugin.install = function (Vue, options) {
  // 1. グローバルメソッドまたはプロパティを追加
  Vue.myGlobalMethod = function () {
    // 何らかのロジック ...
  }
  // 2. グローバルアセットを追加
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 何らかのロジック ...
    }
    ...
  })
  // 3. 1つ、または複数のコンポーネントオプションを注入
  Vue.mixin({
    created: function () {
      // 何らかのロジック ...
    }
    ...
  })
  // 4. インスタンスメソッドを追加
  Vue.prototype.$myMethod = function (methodOptions) {
    // 何らかのロジック ...
  }
}

今回はthis.$eventBusでアクセスできるようにしたいと思います。

実装にあたってすでにある以下のVueプラグインを参考にしました。

GitHub – fffixed/vue-bus: Tiny simple central event bus plugin for Vue.js
GitHub – cklmercer/vue-events: Simple event handling for Vue.js

const eventBus = {
  // 今回は特にオプションは受け取らないのでVueのみの受け取り
  install(Vue) {
    const bus = new Vue()
    // Vue.$eventBusで使えるようにプロトタイプに追加
    Object.defineProperty(Vue.prototype, '$eventBus', {
      get() {
        return bus
      }
    })
    // beforeMountとbeforeDestroyにフックしてemitとoffを管理
    Vue.mixin({
      beforeCreate() {
        if(typeof this.$options.events !== 'object') return
        let eventObj = {}
        for (let key in this.$options.events) {
          eventObj[key] = this.$options.events[key].bind(this)
        }
        this.$once('hook:beforeMount', () => {
          for (let key in eventObj) {
            bus.$on(key, eventObj[key])
          }
        })
        this.$once('hook:beforeDestroy', () => {
          for (let key in eventObj) {
            bus.$off(key, eventObj[key])
          }
          eventObj = null
        })
      },
    })
  }
}
if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(eventBus)
}
export default eventBus

参考にしたプラグインから不要な記述を削除しただけみたいになってますが、一応プラグインとしては動きます。あとはこれをプラグインとしてuseすればthis.$eventBusを通してグローバルにイベントのやり取りが可能になります。

イベント登録するときは

this.$eventBus.$emit('eventName')

登録したイベントはリッスンしたいコンポーネント内で

mounted() {
  this.$eventBus.$on('eventName', this.callback)
}

みたいにすれば使えます!かんたん!

まとめ

今回はイベントのハンドリングを楽にするプラグインを作ってみました。Vueのプラグインは凝ったやつ作るのは大変だけど単純なやつは割とサクッと作れるので色々作ってみてもいいのではないでしょうか!

採用情報

メンバーズエッジで最高のチームで最高のプロダクトを作りませんか?

最高のプロダクトをつくる 最高のチームで働く

在宅でも、地方でも、首都圏でも。多様な働き方で最高のチームをつくり、お客様のプロダクトパートナーを目指します。アジャイル開発を通じ、開発現場の第一線で活躍し続けたいエンジニアを募集しています。