今日は、Vue 3のemitを使って、子コンポーネントの操作を親コンポーネントへ伝える流れを触ったのでメモします。
コンポーネントを分けたときに、どこが値を持ち、どこが操作を通知するのかを少しずつ整理したいです。
まだ途中段階なので、勘違いなどあれば教えていただけるとうれしいです。
今日やったこと
子のResetButtonからイベントを発行し、親のcountをリセットする形を試しました。
今回は、親であるApp.vueがカウントの状態を持ち、子であるResetButton.vueはボタンのクリックを親へ知らせる役割に分けました。
![]() |
「子のボタンを押したのに、数字を変えるのは親なんだね。操作する部品と、データを持つ場所が別なのがポイントかも」 |
親コンポーネントでは、refでcountを用意しました。Incrementボタンは親で直接処理し、子が発行したresetイベントを受け取ったときに、親側でcountを0に戻します。
<script setup>
import { ref } from 'vue'
import ResetButton from './components/ResetButton.vue'
// この画面で使うカウントを親コンポーネントで管理する
const count = ref(0)
</script>
<template>
<!-- テンプレート内ではrefの.valueを書かずに値を表示できる -->
<p>Count: {{ count }}</p>
<!-- 親が持つcountをクリックごとに1増やす -->
<button @click="count++">Increment</button>
<!-- 子が発行したresetイベントを受け、親でcountを0に戻す -->
<ResetButton @reset="count = 0" />
</template>
画面にはカウント表示、Incrementボタン、Resetボタンが表示されます。Incrementで数値を増やし、子コンポーネントのResetを押すと、親が持つ数値を0へ戻す形です。
子のResetButton.vueでは、クリック時に$emit('reset')を呼び出します。
<template>
<!-- resetという名前のカスタムイベントを親へ発行する -->
<button @click="$emit('reset')">Reset</button>
</template>
今の自分には、子は「リセットしてほしい」という合図を出し、実際に値を変える処理は親が担当する仕組みに見えています。ゲームでいうと、ボタンはメニュー画面の操作を受け付け、セーブデータの数値を更新する役目は別の管理側が持つような分担に少し近いと感じました。
Vueでは、子コンポーネントが$emitでカスタムイベントを発行し、親コンポーネントが@イベント名で受け取れます。詳しい書き方はVue.js公式ドキュメントも見返したいです。
つまずいたところ
イベントだけを送る場合と、イベントに値を付けて送る場合の違いで少し迷いました。
イベントと一緒に値を渡す
resetイベントだけなら、親側でcount = 0と決め打ちできます。ただ、100へリセットするような場合は、子から値を渡して親で受け取る書き方もできます。
![]() |
「子は`100`を渡すだけで、その値をどう使うかは親が決めるんだね。受け取ったアイテムを使うか保管するかを、持ち主が決める感じかな」 |
子側では、イベント名の後ろに渡したい値を書きます。
<template>
<!-- resetイベントと一緒に、リセット後の値100を親へ渡す -->
<button @click="$emit('reset', 100)">Reset</button>
</template>
親側では、$eventで子から渡された値を受け取れます。
<template>
<p>Count: {{ count }}</p>
<button @click="count++">Increment</button>
<!-- 子が渡した値を$eventとして受け取り、countへ代入する -->
<ResetButton @reset="count = $event" />
</template>
この書き方なら、子はイベントと値を渡すことに集中できます。親は受け取った値を使って状態を更新するため、値の扱いを親側へ集められるのが分かりやすそうです。
関数で受け取る書き方
@reset="count = $event"は短く書けますが、処理が増えるなら関数にしたほうが読みやすい場面もありそうです。
<script setup>
import { ref } from 'vue'
import ResetButton from './components/ResetButton.vue'
// 親コンポーネントがカウントの状態を持つ
const count = ref(0)
// 子から渡された値を受け取り、親の状態を更新する
function resetCount(newCount) {
count.value = newCount
}
</script>
<template>
<p>Count: {{ count }}</p>
<button @click="count++">Increment</button>
<!-- resetイベントで受け取った値をresetCountへ渡す -->
<ResetButton @reset="resetCount" />
</template>
今はカウントを代入するだけですが、あとから処理を足すならresetCountという名前があることで、何をしているか追いやすくなるかもしれません。
今の理解(メモ)
親が状態を持ち、子は操作をイベントとして通知する流れとして整理しています。
-
refで作ったcountは、画面に表示する値を親コンポーネントで保持するためのもの - 子コンポーネントは
$emit('reset')で、親に「リセット操作があった」と伝える -
emit('reset', 100)のように書くと、イベントと一緒に値も親へ渡せる - 親は
@resetでイベントを受け取り、$eventまたは関数の引数から渡された値を受け取る - 子が親の
countを直接書き換えるのではなく、イベントを通じて親へ判断を任せる形になる
テンプレート内では$emitを使えますが、<script setup>内からイベントを発行したいときは、defineEmitsを使います。
<script setup>
// このコンポーネントがresetイベントを発行することを明示する
const emit = defineEmits(['reset'])
// スクリプト内の処理からイベントを発行する
function emitReset() {
// resetイベントと、リセット後の値100を親へ渡す
emit('reset', 100)
}
</script>
<template>
<!-- クリック時にスクリプト内で定義した関数を実行する -->
<button @click="emitReset">Reset</button>
</template>
defineEmits(['reset'])は、このコンポーネントが発行するイベントを明示するために使うもの、と今は理解しています。ゲームのマップごとに入口と出口を決めておくように、コンポーネントが外へ通知するイベントを先に見える形にしておくと、親子のつながりを追いやすそうです。
イベント名は、複数単語になる場合にreset-countのようなケバブケースを使う形も試してみたいです。
次にやりたいこと
小さな画面操作を増やしながら、イベントと状態の役割分担を確かめたいです。
-
reset-countという複数単語のイベント名を使い、子での発行と親での受け取りを試す - 親から
propsでリセット値を渡し、子が100を固定で持たない形を作る -
ResetButtonを複数置き、それぞれ違うリセット値を親へ渡せるか確認する - ボタンを押したときに確認メッセージを表示する処理を親側へ追加し、関数で受け取る利点を見てみる
- TypeScriptを使う場合の
defineEmitsで、イベント名と渡す値の型をどう書くか調べる
今日の整理
親子コンポーネント間のイベント通信を、今の理解でまとめます。
| 用語・書き方 | 役割 | 利用イメージ | 次に確認したいこと |
|---|---|---|---|
ref(0) |
親が画面表示に使う状態を持つ | カウントの数値を保持・表示する | 状態を親と子のどちらへ置くか |
$emit('reset') |
テンプレート内からイベントを発行する | 子のボタン操作を親へ知らせる | 複数イベントを扱う場合 |
$emit('reset', 100) |
イベントと値を親へ渡す | リセット後の数値を指定する | 複数の値を渡す場合 |
@reset="..." |
親が子のイベントを受け取る | 親でカウントを更新する | 関数へ処理をまとめる場面 |
defineEmits(['reset']) |
子が発行するイベントを明示する | 子コンポーネントの役割を示す | TypeScriptでの型定義 |
おわりに
今回は、emitを使って子コンポーネントから親コンポーネントへ操作を通知する流れを触りました。まだ書き方を見ながら進めている段階ですが、親が値を持ち、子は操作を知らせるという役割分担は少し見えてきました。
コンポーネントを細かく分けるほど、どの部品が何を通知し、どこで状態を変えるのかを整理する必要がありそうです。次はpropsも組み合わせながら、親から子への流れと子から親への流れを並べて確認したいです。
学習中の内容なので、勘違いや理解不足もあると思います。もし気になる点があれば教えてもらえるとうれしいです。
注意事項
本ブログに掲載している内容は、私個人の見解であり、
所属する組織の立場や戦略、意見を代表するものではありません。
あくまでエンジニアとしての経験や考えを発信していますのでご了承ください。
