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 関数を得るマクロ
Payloadevent と一緒に親へ渡す追加データ
Event Listener親が @event-name で event を受け取る処理
Runtime Validationevent payload を実行時に検証する仕組み
One-Way Data Flow親が state を持ち、子は event で変更要求を返す設計原則