言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

  1. トップページ
  2. Vue辞典
  3. defineEmits()

defineEmits()

対応: Vue 3(2020)

『Vue』の defineEmits() は、<script setup> 内で子コンポーネントが発火するイベントを宣言するためのコンパイラマクロです。Options API の emits オプションに相当する機能を、Composition API スタイルで簡潔に記述できます。

構文

<!-- 子コンポーネント側:defineEmits() でイベントを宣言します -->

<template>
  <!-- ボタンをクリックすると emit 関数でイベントを発火します -->
  <button @click="handleClick">送信</button>
</template>

<script setup>
// defineEmits() はインポート不要で使えるコンパイラマクロです
// 返り値の emit 関数を使ってイベントを発火します
const emit = defineEmits(['イベント名']);

function handleClick() {
  // emit(イベント名, 渡すデータ) の形で呼び出します
  emit('イベント名', 渡すデータ);
}
</script>
<!-- 親コンポーネント側:v-on(@)でイベントを受け取ります -->

<template>
  <!-- @イベント名 で子からの通知をハンドラ関数に結びつけます -->
  <MyComponent @イベント名="ハンドラ関数" />
</template>

defineEmits() のオプション一覧

記法概要
配列形式defineEmits(['submit', 'close']) のように文字列の配列でイベント名を宣言します。シンプルな用途に向いています。
オブジェクト形式(バリデーションなし)defineEmits({ submit: null }) のようにイベント名をキーに null を指定します。
オブジェクト形式(バリデーションあり)イベント名をキーに検証関数を指定します。関数が false を返すとコンソールに警告が出ます。引数の妥当性を保証したい場合に使います。

defineEmits() の主な特徴

特徴説明
インポート不要のマクロ<script setup> 内でのみ使えるコンパイラマクロです。import せずに直接呼び出せます。
戻り値が emit 関数になるconst emit = defineEmits([...]) のように戻り値を受け取ることで、script 内のどこからでもイベントを発火できます。
テンプレートでも使用できるテンプレート内では $emit('イベント名', データ) の形でも発火できます。
バリデーションを定義できるオブジェクト形式で宣言すると、発火時に引数が正しいかどうか検証関数でチェックできます。
TypeScript での型指定も可能TypeScript 環境では defineEmits<{ submit: [value: string] }>() のようにジェネリクスで型を指定する書き方もサポートされています。

バリデーション付き宣言

オブジェクト形式でバリデーション関数を渡すと、イベント発火時に引数の値を検証できます。

<script setup>
// オブジェクト形式でバリデーション付きの宣言をします
const emit = defineEmits({
  // submit イベント:value が空文字でないことを検証します
  submit: function(value) {
    if (typeof value !== 'string' || value.trim() === '') {
      // 検証失敗時はコンソールに警告を出します
      console.warn('submit イベントには空でない文字列を渡してください');
      return false;
    }
    return true;
  },

  // close イベント:バリデーションなしの宣言です
  close: null,
});
</script>

サンプルコード

ログインフォームコンポーネントが、入力内容を検証してから親へ送信イベントを通知する例です。

<!-- LoginForm.vue(子コンポーネント) -->

<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <label>メールアドレス</label>
      <!-- v-model で入力値をリアクティブに管理します -->
      <input v-model="email" type="email" placeholder="example@mail.com" />
    </div>

    <div>
      <label>パスワード</label>
      <input v-model="password" type="password" placeholder="パスワードを入力" />
    </div>

    <!-- エラーメッセージがあれば表示します -->
    <p v-if="errorMessage" class="error">{{ errorMessage }}</p>

    <!-- ボタンクリックで handleSubmit が呼ばれます -->
    <button type="submit">ログイン</button>

    <!-- キャンセルボタンは emit で直接発火します -->
    <button type="button" @click="emit('cancel')">キャンセル</button>
  </form>
</template>

<script setup>
import { ref } from 'vue';

// 発火するイベントを宣言します(インポート不要)
// submit:ログイン情報をオブジェクトで渡します
// cancel:キャンセル時に発火します(データなし)
const emit = defineEmits({
  submit: function(payload) {
    // payload に email と password が含まれていることを検証します
    if (!payload || typeof payload.email !== 'string' || typeof payload.password !== 'string') {
      console.warn('submit イベントには email と password を持つオブジェクトを渡してください');
      return false;
    }
    return true;
  },
  cancel: null,
});

// フォームの入力値をリアクティブに管理します
const email    = ref('');
const password = ref('');

// バリデーションエラーのメッセージを管理します
const errorMessage = ref('');

function handleSubmit() {
  // 入力が空でないかクライアント側で確認します
  if (email.value.trim() === '') {
    errorMessage.value = 'メールアドレスを入力してください。';
    return;
  }
  if (password.value.trim() === '') {
    errorMessage.value = 'パスワードを入力してください。';
    return;
  }

  // エラーをクリアします
  errorMessage.value = '';

  // 'submit' イベントとともに入力内容を親へ送ります
  emit('submit', {
    email:    email.value,
    password: password.value,
  });
}
</script>

<style scoped>
/* scoped を付けるとスタイルがこのコンポーネントだけに適用されます */
form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 320px;
}
.error {
  color: #e74c3c;
  font-size: 0.9em;
}
</style>
<!-- App.vue(親コンポーネント) -->

<template>
  <div>
    <!-- ログイン成功後のメッセージを表示します -->
    <p v-if="loggedInUser">ログイン中:{{ loggedInUser }}</p>

    <LoginForm
      v-else
      @submit="handleLogin"
      @cancel="handleCancel"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue';

// LoginForm コンポーネントを import します
// script setup 内で import するだけで template 内で使えるようになります
import LoginForm from './LoginForm.vue';

// ログイン中のユーザーを管理します(null の場合はフォームを表示)
const loggedInUser = ref(null);

function handleLogin(payload) {
  // 子から受け取った email と password で認証処理を行います
  console.log('ログイン処理:', payload.email);

  // ここでは受け取った email をそのまま表示します
  loggedInUser.value = payload.email;
}

function handleCancel() {
  // キャンセル時の処理を記述します
  console.log('ログインをキャンセルしました');
}
</script>

概要

defineEmits()<script setup> で使えるコンパイラマクロであり、インポート不要で利用できます。Options API の emits オプションと同等の機能を持ちながら、Composition API のコンテキストで自然に記述できます。

イベントを明示的に宣言することで、コンポーネントの公開インターフェースが明確になります。また、宣言されたイベントは親の DOM イベントとして誤ってバブリングすることを防げます。子コンポーネントが発火するイベントはすべて defineEmits() で宣言するとよいでしょう。

props の宣言については defineProps を、emit の基本的な概念については emit を、<script setup> の仕組み全体については single_file_component もあわせてご覧ください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。