Vue で子コンポーネントの emit したイベントが親で受け取れない
子の $emit / defineEmits したイベントが親に届かないのは、イベント名の綴り不一致や emits 未宣言が主因。
emit 名と親の @ ハンドラの綴りを揃え、defineEmits で宣言する。
公開: 更新:
要約
子コンポーネントが発火したイベントが親で受け取れない場合、まず疑うのは emit したイベント名と親側 @ ハンドラの綴りの不一致です。
次に多いのが defineEmits での宣言漏れです。
宣言していないイベントは、コンポーネントのルート要素への属性フォールスルー対象になり、ハンドラとして機能しないことがあります。
emit 名・親のハンドラ名・defineEmits の宣言の3つを同じ綴りで揃えるのが基本です。
実行例
綴りの違う名前で emit すると親のハンドラは一度も呼ばれず、受け取ったイベントは空配列のままになる。
名前を揃えれば payload ごと届き、DOM 内テンプレートでも @my-event の kebab-case で受け取れる。
$ node bad.mjs
親が受け取ったイベント: []$ node solve_same_name.mjs
親が受け取ったイベント: [{"ok":true}]
終了コード: 0(payload が入っていれば解消)$ node kebab.mjs
@my-event で受け取れたか: ["x"]— 2026-09-18 時点の出力
検証環境
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
- 綴り不一致:
emit('submit')に対し親が@submmitのようにタイプミスしている - 宣言漏れ:
defineEmitsを書かずにemitだけ呼んでいる - ケース変換の誤解: DOM 内テンプレートで
@myEventと書き、my-eventに変換されず外れる - ネイティブ衝突:
@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(新しいタブで開く)で詳しく説明されています。