始めに
前にVueのイベントの設定に関する話を書きました。
この時はプラグインのファイルをローカルで作って使うようにしてましたが、パッケージにしました。ここではこのパッケージの使用方法について説明します。基本的に使い方は前回の記事と同じですが、少しだけ名前が違っていたりするので注意してください。
作成の背景
過去の記事でも書きましたが改めてこちらでも書きます。Vueのイベント送信ではthis.$emit
を使いますが、これは文字列で定義するため以下のようなエラー問題があります。
- イベント名を間違える
- 親コンポーネントでイベントの設定を忘れる
- イベントになんの引数を入れていいか分からなくなる
<template lang="pug">
div
button(@click="onClick1") click1
button(@click="onClick2") click2
button(@click="onClick3") click3
//- こっちでも送れるからどこで送信しているか探すの大変
button(@click="$emit('click1')") click1
//- click3はnumber, stringを送るべきなのに引数が足りてない
button(@click="$emit('click3', 10)") click3
</template>
<script>
export default {
methods: {
onClick1() {
this.$emit('click1');
},
onClick2() {
// click2のtypoで送信失敗
this.$emit('cilck2', 10);
},
onClick3() {
this.$emit('click3', 10, 'text');
}
}
};
</script>
<template lang="pug">
div
Component(
//- イベントの受け取りでtypoして全くイベントが受け取れない
@cilck1="onClick1",
@click2="onClick2",
//- click3のイベントを設定し忘れる
//- @click3="onClick3",
//- そんなイベントは設定していないのでイベントは受け取れない
@click4="onClick4"
)
</template>
<script>
import Component from './Component.vue';
export default {
component: {
Component
},
methods: {
onClick1() {
},
onClick2(value) {
console.log(value);
},
// 本当にvalue, textであっていたか調べにくい
onClick3(value, text) {
console.log(value, text);
},
onClick4(value, text) {
console.log(value, text);
}
}
};
</script>
これの問題を解決するためにoptionにevents
を用意し、そこで設定したメソッドだけ呼び出すプラグインを作りました。
使用方法
まずパッケージをインストールします。
$ yarn add @wintyo/vue-event-emission-plugin
プラグインとしてインストールします。
import Vue from 'vue';
import EventEmissionPlugin from '@wintyo/vue-event-emission-plugin';
Vue.use(EventEmissionPlugin);
Vueコンポーネントのevents
というところに$emit
したい名前の関数を用意し、その名前でthis.$events.~
で実行すると、内部でその名前と同じ名前でthis.$emit
します。
event情報は全てevents
で定義しているため、methodsに書こうがtemplateに書こうが送信するイベント一覧は見失わなくなると思います。
<template lang="pug">
div
p method emit
button(@click="click1") action1
button(@click="click2") action2
button(@click="click3") action3
br
p direct emit
button(@click="$events.click2(0)") action2
</template>
<script>
export default {
// $emitされるイベント一覧(親コンポーネントが受け取れるイベントが一目でわかる)
events: {
click1: () => [],
click2: (value) => [value],
click3: (value, text) => [value, text]
// 内部ではこんな感じでキー名と一致したeventsの関数の実行結果を$emitしている
// const key = 'click1';
// this.$emit(key, ...events[key](...args));
},
methods: {
click1() {
this.$events.click1();
},
click2() {
this.$events.click2(10);
},
click3() {
this.$events.click3(20, 'test');
}
}
}
</script>
イベントのバリデーションチェック
今回作成したプラグインは以下のようなチェックをします。
- 子コンポーネントで実行する
$events.~
の引数の数が合わない時に警告(型チェックまでは無理でした) - 親コンポーネントでlistenerの受け取り忘れを警告
- 子コンポーネントに存在しないevent名をlistenerに登録した場合に警告
- 子コンポーネントで定義していないeventを実行した場合は実行時エラー(そもそもメソッドがないので)
例
<template lang="pug">
div
EventEmission(
@click1="onClick1",
@click2="onClick2",
@click3="onClick3",
//- unhandleEventは`EventEmit`で定義されていないので警告が出る
@unhandleEvent=""
)
</template>
<script>
import EventEmission from './components/EventEmission.vue';
export default {
name: "App",
components: {
EventEmission
},
methods: {
onClick1() {
console.log('click1');
},
onClick2(value) {
console.log('click2', value);
},
onClick3(value, text) {
console.log('click3', value, text);
}
}
};
</script>
<template lang="pug">
div
p method emit
button(@click="click1") action1
button(@click="click2") action2
button(@click="click3") action3
br
p direct emit
button(@click="$events.click2(0)") action2
p no match emit
//- click3はvalue, textを渡す必要があるので警告が出る
button(@click="$events.click3()") action3
p unhandled emit
//- click4はApp.vue側で設定されていないので警告が出る
button(@click="$events.click4()") action4
</template>
<script>
export default {
events: {
click1: () => [],
click2: (value) => [value],
click3: (value, text) => [value, text],
click4: () => []
},
methods: {
click1() {
this.$events.click1();
},
click2() {
this.$events.click2(10);
},
click3() {
this.$events.click3(20, 'test');
}
}
}
</script>
実行結果
イベントの設定が上手くいっていない場合は親コンポーネント、子コンポーネント、イベント名が警告として出力されます。
(click3の警告は実際にイベントが発生した時じゃないと出ません)
オプション設定
今のままだとイベントの設定が必須になってしまうので、以下のようにしてオプションで設定できるようにしました。
<script>
export default {
events: {
click1: () => [],
click2: (value) => [value],
click3: (value, text) => [value, text],
click4: () => [],
// オブジェクトで渡す
optionalEvent: {
emit: (value) => [value],
optional: true // オプション設定
}
},
};
</script>
できなかったこと
型定義
Vue.jsのoptions APIの型推論を調べてみましたが、かなり複雑でとても推論できるようなものではありませんでした(汗)。なので残念ながらTypeScriptでもanyです。まぁでもイベント名を誤った場合はメソッドが呼べずに実行時エラーになって気づくのでとりあえず大丈夫だと思っています。
終わりに
以上でプラグインの紹介でした。イベントの設定に悩んでいる方はぜひ使ってみてください。