このページは、コンポーネントの基本をすでに読んでいることを前提としています。コンポーネントを初めて使用する場合は、最初にそれをお読みください。
イベントの送信とリスニング
$emitを使用してコンポーネントからv-on
でバインドし、テンプレートにカスタムイベントを直接設定することができます。
<!-- MyComponent --> <button @click="$emit('someEvent')">click me</button>
次に、親はv-onを使用して子のcomponentから発信されたイベントを聞くことができます。
<MyComponent @some-event="callback" />
修飾子は、.once
コンポーネントイベントリスナーでもサポートされています。
<MyComponent @some-event.once="callback" />
コンポーネントやPropsと同様に、イベント名はケースの自動変換を提供します。キャメルケースイベントを使用しても、親のケバブケースのリスナーを使用してそれをどちらでもVueで同じものとして扱われることに注意してください。Propsのケーシングと同様に、テンプレートではケバブケースのイベントリスナーを使用することをお勧めします。
ヒント
ネイティブDOMイベントとは異なり、コンポーネントが発行するイベントはバブルしません。直接の子コンポーネントによって実行されたイベントのみを聞くことができます。
イベント引数
イベントで特定の値を実行すると便利な場合があります。たとえば<BlogPost>
、テキストをどれだけ拡大するかをコンポーネントが担当するようにしたい場合があります。$emit
そのような場合、この値を提供するために追加の引数を渡すことができます。
<button @click="$emit('increaseBy', 1)"> Increase by 1 </button>
次に、親でイベントをリッスンするときに、インライン関数をリスナーとして使用できます。これにより、イベント引数にアクセスできます。
<MyButton @increase-by="(n) => count += n" />
または、イベントハンドラーがメソッドの場合:
<MyButton @increase-by="increaseCount" />
次に、その値がそのメソッドの最初のパラメーターとして渡されます。
function increaseCount(n) { count.value += n }
ヒント
イベント名の後に渡されたすべての追加の引数は$emit()
、リスナーに転送されます。たとえば$emit('foo', 1, 2, 3)
、listener関数では3つの引数を受け取ります。
Emitイベントの宣言
defineEmits()
放出されたイベントは、マクロを介してコンポーネントで明示的に宣言できます。
<script setup> defineEmits(['inFocus', 'submit']) </script>
で使用した$emit
メソッドは、コンポーネント<template>
のセクション内ではアクセスできませんが、代わりに使用できる同等の関数を返します。<script setup>
defineEmits()
<script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() { emit('submit') } </script>
defineEmits()
マクロは関数内では使用できません<script setup>
。上記の例のように、マクロ内に直接配置する必要があります。
setup
の代わりに明示的な関数を使用している場合は、オプション<script setup>
を使用してイベントを宣言する必要があります。
export default { emits: ['inFocus', 'submit'], setup(props, ctx) { ctx.emit('submit') } }
setup()
をしようした場合はコンテキストは他のプロパティと同様です。
export default { emits: ['inFocus', 'submit'], setup(props, { emit }) { emit('submit') } }
このemits
オプションは、オブジェクト構文もサポートしています。これにより、実行されたイベントのペイロードの実行時検証を実行できます。
<script setup> const emit = defineEmits({ submit(payload) { // return `true` or `false` to indicate // validation pass / fail } }) </script>
TypeScriptをで使用している場合は<script setup>
、純粋な型アノテーションを使用して発行されたイベントを宣言することもできます。
<script setup lang="ts"> const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() </script>
オプションですが、コンポーネントがどのように機能するかをより適切に文書化するために、発行されたすべてのイベントを定義することをお勧めします。また、Vueが既知のリスナーをフォールスルー属性から除外することもできます。
ヒント
オプションでネイティブイベント(たとえばclick
)が定義されているemits
場合、リスナーはコンポーネントから送信されたイベントのみをリッスンし、ネイティブイベントに応答しなくなります。
イベントの検証
プロップタイプの検証と同様に、発行されたイベントは、配列構文ではなくオブジェクト構文で定義されている場合に検証できます。
検証を追加するために、イベントには、呼び出しに渡された引数を受け取り、イベントが有効かどうかを示すブール値を返す関数が割り当てられます。emit
<script setup> const emit = defineEmits({ // No validation click: null, // Validate submit event 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>
v-model
とEmitの使い方
カスタムイベントを使用して、v-modelと連携できるカスタムインプットを作ることもできます。
<input v-model="searchText" />
上記のコードは下のものと同じことを意味します。
<input :value="searchText" @input="searchText = $event.target.value" />
コンポーネントで使用する場合、少し違う動きが発生し、下記のようになります。
<CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" />
ただし、これを実際に機能させるには、<input>のコンポーネントの内部で次のことを行う必要があります。
value
属性をmodelValue
プロップにバインドします- で、新しい値でイベントを発行
input
しますupdate:modelValue
これが実際の動作です。
<!-- CustomInput.vue --> <script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
これv-model
で、このコンポーネントで完全に機能するはずです。
<CustomInput v-model="searchText" />
v-model
このコンポーネント内に実装する別の方法はcomputed
、ゲッターとセッターの両方で書き込み可能なプロパティを使用することです。メソッドはプロパティをget
返し、メソッドは対応するイベントを発行する必要があります。modelValue
set
<!-- CustomInput.vue --> <script setup> import { computed } from 'vue' const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) const value = computed({ get() { return props.modelValue }, set(value) { emit('update:modelValue', value) } }) </script> <template> <input v-model="value" /> </template>
v-model
引数
デフォルトでv-model
は、コンポーネントmodelValue
で小道具およびupdate:modelValue
イベントとして使用されます。引数を渡してこれらの名前を変更できますv-model
:
<MyComponent v-model:title="bookTitle" />
この場合、子コンポーネントはtitle
小道具を期待しupdate:title
、親の値を更新するイベントを発行する必要があります。
<!-- MyComponent.vue --> <script setup> defineProps(['title']) defineEmits(['update:title']) </script> <template> <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" /> </template>
複数v-model
のバインディング
v-model
以前に引数で学習した特定の小道具とイベントをターゲットにする機能を活用することで、単一のコンポーネントインスタンスに複数のvモデルバインディングを作成できるようになりました。
各v-modelは、コンポーネントに追加のオプションを必要とせずに、異なるプロップに同期します。
<UserName v-model:first-name="first" v-model:last-name="last" />
<script setup> defineProps({ firstName: String, lastName: String }) defineEmits(['update:firstName', 'update:lastName']) </script> <template> <input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)" /> <input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)" /> </template>
v-model
修飾子(モディファイヤ)
フォーム入力バインディングについて学習していたときに、.trim.number.lazyのような修飾子v-model
が組み込まれていることを確認しました。ただし、場合によっては、独自のカスタム修飾子を追加することもできます。
バインディングcapitalize
によって提供される文字列の最初の文字を大文字にするカスタム修飾子の例を作成しましょう。v-model
<MyComponent v-model.capitalize="myText" />
コンポーネントに追加されたモディファイヤは、プロップv-model
を介してコンポーネントに提供されます。次の例では、デフォルトで空のオブジェクトにmodelModifiers
なるpropを含むコンポーネントを作成しました。
<script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) defineEmits(['update:modelValue']) console.log(props.modelModifiers) // { capitalize: true } </script> <template> <input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
コンポーネントのmodelModifiers
propにが含まれていてcapitalize
、その値がtrue
であることに注意してください。これは、v-model
バインディングに設定されているためv-model.capitalize="myText"
です。
プロップが設定されたので、modelModifiers
オブジェクトキーを確認し、出力された値を変更するハンドラーを作成できます。<input />
以下のコードでは、要素がイベントを発生させるたびに文字列を大文字にしますinput
。
<script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) const emit = defineEmits(['update:modelValue']) function emitValue(e) { let value = e.target.value if (props.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } emit('update:modelValue', value) } </script> <template> <input type="text" :value="modelValue" @input="emitValue" /> </template>
v-model
引数と修飾子の両方を持つバインディングの場合、生成されるプロップ名はarg + "Modifiers"
になります。例えば:
<MyComponent v-model:title.capitalize="myText">
対応する宣言は次のようになります。
const props = defineProps(['title', 'titleModifiers']) defineEmits(['update:title']) console.log(props.titleModifiers) // { capitalize: true }