Vue.jsで親コンポーネントに対して子コンポーネント上でイベントが起きたと通知する仕組みにemitがあります。
API — Vue.js#$emit
Vue.component('welcome-button', {
// $emit('welcome')で親コンポーネントにwelcomeという名のイベント発生を通知します
template: `
<button v-on:click="$emit('welcome')">
Click me to be welcomed
</button>
`
})
<div id="emit-example-simple">
// welcomeイベント発生時にsayHiメソッドを実行します
<welcome-button v-on:welcome="sayHi"></welcome-button>
</div>
Vue.jsでは時折HTMLElementをラッピングしたコンポーネントがあり、そういったコンポーネントに望まれるイベントはラップ対象のイベントそのままです。例えば次です。
<template>
<label>
{{ label }}
<input
type="text"
@click="$emit('click', $event)"
@change="$emit('change', $event)"
@input="$emit('input', $event)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)"
@keydown="$emit('keydown', $event)"
@keypress="$emit('keypress', $event)"
@keyup="$emit('keyup', $event)"
>
</label>
</template>
<script>
export default {
props: {
label: { type: String, default: "" },
},
};
</script>
上述のコードでは途中で切り上げましたが全てのイベントを通知しようとするときりがありません。また、このイベント通知はVue.js内のJavaScriptコードの実行であり計算資源の無駄です(具体的にどの程度資源を使うのか確かめていないので無視できる程度の無駄づかいかも?)。加えて全てのイベントを使う親コンポーネントはまずありません。そういった背景から子コンポーネント中のあるHTMLElementのイベントの内どれを通知するかの定義を親コンポーネントの側で定義できるようにする需要があります。これは次の様に実現できます。
使用したVue.jsの機能はコンポーネントや要素を参照するための機能であるrefです。これを用いてテンプレート内のHTMLElementを参照して”親コンポーネントにイベントを通知するイベント”をHTMLElementに直に取り付けます。
API — Vue.js#$refs
以下コードの抜粋です。
/** 親コンポーネントのtemplate部 */
// 親コンポーネントでは子コンポーネントでemitしてほしいイベント名を配列で渡し
// それぞれのイベントで起きることを定義します
<div style="display:flex; flex-direction:column">
<input-wrapper
label="1つ目"
:emit-events="['click', 'keydown']"
@click="msg='1つ目のclickイベント発火'"
@keydown="msg='1つ目のkeydownイベント発火'"
/>
<input-wrapper
label="2つ目"
:emit-events="['focus', 'change']"
@focus="msg='2つ目のfocusイベント発火'"
@change="msg='2つ目のchangeイベント発火'"
/>
<input-wrapper
label="3つ目"
:emit-events="['focus', 'change']"
@change="msg='3つ目のchangeイベント発火'"
@focus="msg='3つ目のfocusイベント発火'"
/>
</div>
</div>
/** 子コンポーネント */
<template>
<label>
{{ label }}
<!-- refで参照 -->
<input type="text" ref="inputRef">
</label>
</template>
<script>
export default {
props: {
label: { type: String, default: "" },
emitEvents: { // emitするイベント名たちを配列で受け取ります。デフォルトでよく使うイベントを入れておくと便利
type: Array,
default: () => ["click", "change"]
}
},
mounted() {
this.emitEvents.forEach(eventName => {
// addEventLisnerで参照した生のHTMLElementに起きたイベントをそのまま親コンポーネントに流す処理を追加
this.$refs.inputRef.addEventListener(eventName, originalEvent => {
this.$emit(eventName, originalEvent);
});
});
}
};
</script>