概要
Vue.js で 親コンポーネントの method 実行させたい場合、$emit
使ってイベントを発火させるより、
props
に method をコールバックとして登録しておいて実行させたほうが以下のメリット上げられるので「こっちのほうが良くね?」って話です
props
の成約をつけられる(requird, etc)
$emit
の文字列を管理しなくていい
- IDEで補完が効く
実際のコード
ボタン押したらカウントアップしていくようなやつ

呼び出し側の親コンポーネント
<template>
<div>
<div>{{ count }}</div>
<child
:handle-add-number="addCount"
@addNumber="addCount"
/>
</div>
</template>
<script>
import child from "./child";
export default {
components: {child},
data: () => ({
count: 0
}),
methods: {
addCount(number) {
return this.count += number
}
}
}
</script>
子コンポーネント
<template>
<button @click="countUpProps()">count up props</button>
<button @click="countUpEmit()">count up emit</button>
</template>
<script>
export default {
props: {
handleAddNumber: {
type: Function,
required: true
}
},
methods: {
countUpProps() {
this.handleAddNumber(1)
},
countUpEmit() {
this.$emit('addNumber', 1)
}
}
}
</script>
props で定義しておけばこんなふうにIDEで補完が効きます

こんなかんじで、props
を使うと、
requird で縛れて、method の渡し忘れを防げたり、
$emit
の文字列管理しなくていいかつ、IDEで補完が効くのでその分typo が防げる
という点で開発しやすくなるかなと思います。
TS でやると
Vue も Vue3 から、TypeScript を正式にサポートということで未来を見据えて、
同じ処理を Vue3 + TypeScript で書いてみます
呼び出し側の親コンポーネント
<template>
<div>
<p>{{ count }}</p>
<child
:handle-add-num="addNumber"
@addNumber="addNumber"
/>
</div>
</template>
<script lang="ts">
import {defineComponent, ref} from 'vue';
import child from "./child.vue";
import AddNumberInterface from "../types/AddNumberInterface";
export default defineComponent({
components: {
child
},
setup() {
const count = ref<number>(0)
const addNumber: AddNumberInterface = (num: number) => {
return count.value += num
}
return {
count,
addNumber
}
}
})
</script>
props で渡す関数の Interface 定義
export default interface AddNumberInterface {
(num: number): number
}
子コンポーネント
<template>
<div>
<button @click="countUpProps()">count up props</button>
<button @click="countUpEmit()">count up emit</button>
</div>
</template>
<script lang="ts">
import { PropType, defineComponent , SetupContext} from 'vue';
import AddNumberInterface from "../types/AddNumberInterface";
type Props = {
handleAddNum: AddNumberInterface;
}
export default defineComponent({
props: {
handleAddNum: {
type: Function as PropType<AddNumberInterface>,
required: true
}
},
setup(props: Props, context: SetupContext) {
const countUpProps = () => {
props.handleAddNum(1)
}
const countUpEmit = () => {
context.emit('addNumber', 1)
}
return {
countUpProps,
countUpEmit,
}
}
})
</script>
PropType(これは Vue2.6 からあったはず?) と TypeScript を使うことにより、props
の Typeを独自のInterfaceに変更することができ、
どんな functionを渡せばいいか、明示することができました!
「Vue ぽくない」とか言われそうですが、型
という概念が好きな私にとっては、とても書きやすく感じました。
まとめ
公式のリファレンスや、色々な書籍でもVueで親のmethod を実行した場合は、$emit
を使う、
というのは当たり前のように記載されていますが、実際書き比べてみたり、実際のコードを運用してみた観点からしても、
props で method を渡したほが、明示的かつケアレスミスを減らすことができて、とても良いと感じています。
$emit
を使う意義を感じなくなってきているので、$emit
を使う理由や、もっとよい方法があれば教えてもらえるとありがたいです。
未来のことはわかりませんが、Vue3 からは TypeScript サポートされより型を意識した開発をするように今後なっていくのだとしたら、
上記のような書き方はさらに恩恵を受けそうだなと思っています。