できない.dev

Vue で子コンポーネントの emit したイベントが親で受け取れない

子の `$emit` / `defineEmits` したイベントが親に届かないのは、イベント名の綴り不一致や emits 未宣言が主因。
emit 名と親の `@` ハンドラの綴りを揃え、`defineEmits` で宣言する。

#vue#emit#events#composition-api

公開:

要約

子コンポーネントが発火したイベントが親で受け取れない場合、まず疑うのは emit したイベント名と親側 @ ハンドラの綴りの不一致です。

次に多いのが defineEmits での宣言漏れです。
宣言していないイベントは、コンポーネントのルート要素への属性フォールスルー対象になり、ハンドラとして機能しないことがあります。

emit 名・親のハンドラ名・defineEmits の宣言の3つを同じ綴りで揃えるのが基本です。

よくある原因

  1. 綴り不一致: emit('submit') に対し親が @submmit のようにタイプミスしている
  2. 宣言漏れ: defineEmits を書かずに emit だけ呼んでいる
  3. ケース変換の誤解: DOM 内テンプレートで @myEvent と書き、my-event に変換されず外れる
  4. ネイティブ衝突: @click など組み込みイベント名と同名にしてしまう

解決策

1. emit を宣言して名前を揃える

<script setup> では defineEmits で発火するイベントを宣言し、その名前で emit します。

<script setup>
const emit = defineEmits(['submit'])
function onClick() {
  emit('submit', { ok: true })
}
</script>
 
<template>
  <button @click="onClick">送信</button>
</template>

親側は同じ綴りで受けます。

<template>
  <MyForm @submit="handleSubmit" />
</template>

2. DOM 内テンプレートでは kebab-case

ビルドを通さず HTML に直接書く場合、ブラウザが属性名を小文字化するため @my-event のように kebab-case で受ける必要があります。
SFC(.vue)ではどちらの綴りでも一致します。

イベント名の扱いは公式ガイドの Component Eventsで詳しく説明されています。

この記事は役立ちましたか?