Vue.jsで直接の親子関係がないコンポーネント間でデータのやりとりをしたい時がありました。
Vuexでやれよと言われたらそれまでですが、Vuexに頼りたくなかったこともあり、今回Mittというライブラリを使用してグローバルでイベントを取得できるようにしました。
Vue.js2系まではEventBusの$onと$off等を使えばグローバルでイベントを取得することができましたがVue.js3系ではこれが廃止されています。
公式ドキュメントでも代替案としてVue.js3系ではEvent Emitter (イベントエミッタ) インタフェースを実装したMittやtiny-emitterのような外部ライブラリの使用を推奨しているようです。
Mittをインストールする
npm install --save mitt
Mittの使い方
基本的に公式ドキュメントに記載されている通りで問題ないです。
emitter.emitでイベントを待機させておきemitter.onでイベントを読みにいきます。
Vue.jsのemitを使っていればあまり違和感なく使えるかと思います。
まずはemitter.js等の命名でmittのインスタンスを作るファイルを用意しておくとどこでも呼び出せるので便利です。
今回はある親子関係にないコンポーネント間でクリックが押された時のイベントを取得したかったのでemitter.emitでイベントを発行しておきます。
import mitt from 'mitt'
export const emitter = mitt()
/**
* グローバルでクリックイベントを取得する
* @param {object} e
*
* @return {object}
*/
export const handleClick = (e) => {
emitter.emit('handleClick', e)
}
ボタンを押されたときにイベントが発行されるようにクリックメソッドを登録しておきます。
compositionAPIでも同じ要領で問題ないです。
<template>
<div>
<button @click="click($event)">ボタン<button/>
</div>
</template>
<script>
import { handleClick } from 'emitter.js'
export default {
methods: {
click (e) {
handleClick(e)
}
}
}
</script>
クリックイベントを見たいコンポーネントではmountedのタイミングでemitter.onを使用してイベントを取得できるように購読しておきます。
import { emitter } from 'emitter.js'
export default {
mounted () {
emitter.on('handleClick', e => {
if (!Object.keys(e).length) {
return false
}
console.log(e)
})
},
}
これによってComponentA.vueで発行されたイベントがComponentB.vueのコンポーネントで読みに行くことができるようになったかと思います。
Vuexのstoreを利用するにはちょっと歯がゆい場面などで中々重宝しましたので是非使ってみてください。