本文へジャンプ

コンポーネントのイベント

このページは、すでにコンポーネントの基礎を読んでいることを前提にしています。初めてコンポーネントに触れる方は、まずそちらをお読みください。

イベントの発行と購読

コンポーネントは、組み込みの $emit メソッドを使用して、テンプレート式(例: v-on ハンドラー内)で直接カスタムイベントを発行できます:

template
<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>

$emit() メソッドは、コンポーネントインスタンス上でも this.$emit() として使用できます:

js
export default {
  methods: {
    submit() {
      this.$emit('someEvent')
    }
  }
}

そして、親コンポーネントは v-on を使ってイベントを購読できます:

template
<MyComponent @some-event="callback" />

.once 修飾子は、コンポーネントのイベントリスナーでもサポートされています:

template
<MyComponent @some-event.once="callback" />

コンポーネントやプロパティと同様に、イベント名も自動的な大文字・小文字の変換を提供します。キャメルケースのイベントを発行しましたが、親ではケバブケースのリスナーを使用して購読できることに注意してください。プロパティ名での大文字・小文字の使い分けと同様に、テンプレートではケバブケースのイベントリスナーを使用することをお勧めします。

TIP

ネイティブの DOM イベントとは異なり、コンポーネントから発行されたイベントはバブリングしません。直接の子コンポーネントから発行されたイベントのみを購読できます。兄弟コンポーネントや深くネストしたコンポーネント間で通信する必要がある場合は、外部のイベントバスやグローバルな状態管理ソリューションを使ってください。

イベントの引数

イベントで特定の値を発行すると便利な場合があります。例えば、 <BlogPost> コンポーネントに、テキストをどれだけ拡大するかを担当させたい場合があります。そのような場合、$emit に追加の引数を渡して値を提供できます:

template
<button @click="$emit('increaseBy', 1)">
  Increase by 1
</button>

次に、親でイベントを購読する際に、リスナーとしてインラインのアロー関数を使用することで、イベントの引数にアクセスできます:

template
<MyButton @increase-by="(n) => count += n" />

または、イベントハンドラーがメソッドの場合は:

template
<MyButton @increase-by="increaseCount" />

その値はそのメソッドの最初のパラメーターとして渡されます:

js
methods: {
  increaseCount(n) {
    this.count += n
  }
}
js
function increaseCount(n) {
  count.value += n
}

TIP

$emit() に渡されたイベント名の後にあるすべての追加の引数はリスナーに転送されます。たとえば $emit('foo', 1, 2, 3) とすると、リスナー関数は 3 つの引数を受け取ります。

発行するイベントの宣言

defineEmits() マクロemits オプションを使用して、コンポーネントが発行するイベントを明示的に宣言できます:

vue
<script setup>
defineEmits(['inFocus', 'submit'])
</script>

<template> で使用した $emit メソッドは、コンポーネントの <script setup> セクション内ではアクセスできませんが、代わりに defineEmits() が同等の関数を返してくれるので、それを使用できます:

vue
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick() {
  emit('submit')
}
</script>

defineEmits() マクロは関数の中では使用できません。上記の例のように、<script setup> 内に直接記述する必要があります。

<script setup> の代わりに明示的な setup 関数を使う場合は、イベントは emits オプションを使って宣言する必要があり、emit 関数は setup() コンテキスト上で公開されます:

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  }
}

setup() コンテキストの他のプロパティと同様に、emit は安全に分割代入できます:

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, { emit }) {
    emit('submit')
  }
}
js
export default {
  emits: ['inFocus', 'submit']
}

emits オプションはオブジェクト構文もサポートしており、発行されたイベントのペイロードのランタイムバリデーションを実行できます:

vue
<script setup>
const emit = defineEmits({
  submit(payload) {
    // バリデーションの合格/不合格を示す
    // `true` または `false` を返す
  }
})
</script>

<script setup> で TypeScript 使用している場合、純粋な型アノテーションを使用して、発行するイベントを宣言することもできます:

vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

詳細: コンポーネントの emit の型付け

js
export default {
  emits: {
    submit(payload) {
      // バリデーションの合格/不合格を示す
      // `true` または `false` を返す
    }
  }
}

参照: コンポーネントの emit の型付け

任意ですが、コンポーネントがどのように動作すべきかをよりよく文書化するために、発行されるすべてのイベントを定義することが推奨されます。また、これにより Vue は既知のリスナーをフォールスルー属性から除外し、サードパーティのコードによって手動でディスパッチされた DOM イベントによって起こるエッジケースを回避できます。

TIP

ネイティブイベント(例: click)が emits オプションに定義されている場合、リスナーはコンポーネントが発行する click イベントのみを購読し、ネイティブの click イベントには反応しなくなります。

イベントのバリデーション

発行するイベントは、プロパティの型バリデーションと同様に、配列構文ではなくオブジェクト構文で定義されている場合にバリデーションできます。

バリデーションを追加するには、「this.$emitemit の呼び出しに渡された引数」を受け取り、「イベントが正当かどうかを示す真偽値」を返す関数をイベントに割り当てます。

vue
<script setup>
const emit = defineEmits({
  // バリデーションなし
  click: null,

  // submit イベントをバリデーション
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>
js
export default {
  emits: {
    // バリデーションなし
    click: null,

  // submit イベントをバリデーション
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}
コンポーネントのイベントが読み込まれました