Events - 子から親への通知契約
ロードマップ: Vue.js学習ロードマップ
What(何についてか)
component events は、子コンポーネントが親コンポーネントへ出来事や変更要求を通知するための仕組みである。props が親から子への入力であるのに対し、events は子から親への通知経路になる。
Why(なぜ必要か)
Vue では、子コンポーネントが親の state を直接変更しない。一方向データフローを保つため、子は「何が起きたか」を event として親へ通知し、親がその通知を受けて state を更新する。
- 子が親の state 所有権を侵害しない
- 親子通信の方向を明確に分離できる
- props と組み合わせて公開APIを整理できる
- TypeScript により payload 契約を安全に扱える
How(どう動くか)
Emitting and Listening to Events
子コンポーネントは $emit() または defineEmits() で event を発火し、親は v-on(@)で受け取る。
<!-- Child -->
<button @click="$emit('someEvent')">Click Me</button><!-- Parent -->
<MyComponent @some-event="callback" />event 名は camelCase で emit しても、親テンプレートでは kebab-case で受けるのが標準的である。
component event は DOM event のようにバブリングしない。直接の親だけが listen できる。
Event Arguments
event には payload を載せられる。$emit() / emit() の第2引数以降が listener に渡される。
<button @click="$emit('increaseBy', 1)">
Increase by 1
</button><MyButton @increase-by="(n) => count += n" />複数値を渡すことも可能だが、意味のあるデータが増える場合は1つのオブジェクトにまとめた方が保守しやすい。
emit('submit', {
email,
password
})Declaring Emitted Events
<script setup> では defineEmits() を用いて、コンポーネントがどの event を外部へ公開するかを宣言できる。
<script setup>
const emit = defineEmits(['inFocus', 'submit'])
function buttonClick() {
emit('submit')
}
</script>TypeScript を使う場合は、型ベース宣言が実務的に扱いやすい。
<script setup lang="ts">
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
</script>この形式では、子コンポーネントがどの event を emit し、どの型の payload を渡すかを公開APIとして明示できる。
Events Validation
defineEmits() はオブジェクト形式で event payload の runtime validation も記述できる。
<script setup>
const emit = defineEmits({
submit: ({ email, password }) => {
return !!(email && password)
}
})
</script>ただし、実アプリケーションでは validator に業務ルールまで持ち込むと責務が重くなりやすい。型宣言中心で十分なケースが多い。
実務上の判断軸
flowchart TD A["子コンポーネントで状態変化が起きた"] --> B{"親へ伝える必要があるか"} B -->|"ない"| C["子の内部状態で完結"] B -->|"ある"| D["emit で event を通知"] D --> E["単純な値ならそのまま payload"] D --> F["複数項目なら object payload"] D --> G["defineEmits で契約を明示"] G --> H["基本は TypeScript 型ベース宣言"]
props との対比
| 方向 | 仕組み | 役割 |
|---|---|---|
| 親 → 子 | props | 入力を渡す |
| 子 → 親 | events | 出来事や変更要求を通知する |
props と emits を合わせて読むと、そのコンポーネントの公開APIが把握しやすい。
TypeScript と validator の住み分け
TypeScript ベースの defineEmits<...>() は、event 名と payload 形状をインターフェースとして明示する用途に向く。これは公開API定義として自然である。
一方、オブジェクト形式 validator は payload の runtime validation を行えるが、業務ルールや入力妥当性まで子コンポーネントが抱えると責務が重くなりやすい。
- 基本は
defineEmits<...>()を優先 - validator は限定的なガード用途で使う
- 複雑な検証は別レイヤーや Zod 等のスキーマで扱う方が分離しやすい
Options API → Composition API 差分
| 項目 | Options API(旧) | Composition API(新) |
|---|---|---|
| event発火 | this.$emit('submit') | const emit = defineEmits(...); emit('submit') |
| 契約定義 | emits: ['submit'] | defineEmits() で setup 冒頭に明示 |
| 型付け | 記述は可能だが相対的に冗長 | <script setup lang="ts"> で自然に型宣言できる |
| payload設計 | methods 側に分散しやすい | setup 冒頭で event API を集約しやすい |
Key Concepts
| 用語 | 説明 |
|---|---|
| Component Event | 子コンポーネントが親へ通知するためのカスタムイベント |
$emit() | テンプレートや Options API で event を発火する手段 |
defineEmits() | <script setup> で emit 契約を宣言し、emit 関数を得るマクロ |
| Payload | event と一緒に親へ渡す追加データ |
| Event Listener | 親が @event-name で event を受け取る処理 |
| Runtime Validation | event payload を実行時に検証する仕組み |
| One-Way Data Flow | 親が state を持ち、子は event で変更要求を返す設計原則 |