Как обрабатывать события от дочернего компонента?

Во Vue события от дочернего компонента обрабатываются с помощью механизма всплытия событий и метода $emit, который используется внутри дочернего компонента для отправки события наружу. Родительский компонент может "слушать" эти события и выполнять нужную логику. Этот подход позволяет дочернему компоненту быть более изолированным и переиспользуемым, а родителю — управлять поведением на основе действий дочернего компонента.

Основной механизм: $emit

Дочерний компонент с помощью метода this.$emit отправляет событие. Родитель "подписывается" на это событие при использовании компонента, указывая обработчик через директиву v-on или сокращённый синтаксис @.

Простой пример

Дочерний компонент: ChildComponent.vue

<template>
<button @click="handleClick">Нажми меня</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('childClicked', 'Привет от дочернего компонента')
}
}
}
</script>

Родительский компонент: ParentComponent.vue

<template>
<div>
<ChildComponent @childClicked="onChildClicked" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
methods: {
onChildClicked(message) {
console.log('Получено событие:', message)
}
}
}
</script>

В этом примере при клике на кнопку в дочернем компоненте генерируется событие childClicked, и родитель его ловит с помощью @childClicked.

Передача данных через событие

События можно вызывать с аргументами — любыми значениями: строками, числами, объектами, массивами и т.д. Эти значения будут переданы в обработчик события родителя.

this.$emit('saveData', { name: 'Alex', age: 32 })

Родитель:

<ChildComponent @saveData="handleSave" />
methods: {
handleSave(payload) {
console.log(payload.name) // Alex
}
}

Несколько аргументов

Можно передать несколько аргументов:

this.$emit('someEvent', arg1, arg2)

Родитель:

<ChildComponent @someEvent="(a, b) => doSomething(a, b)" />

Названия событий

Названия событий могут быть произвольными, но часто используют kebab-case, например:

this.$emit('item-selected')

Родитель:

<ChildComponent @item-selected="handleSelect" />

Или camelCase, но тогда лучше использовать v-on вместо @:

<ChildComponent v-on:itemSelected="handleSelect" />

Пример: форма и кнопка

Дочерний компонент SubmitButton.vue

<template>
<button @click="submit">Отправить</button>
</template>
<script>
export default {
methods: {
submit() {
this.$emit('submitForm', { id: 1, status: 'ready' })
}
}
}
</script>

Родитель

<SubmitButton @submitForm="handleSubmit" />
methods: {
handleSubmit(data) {
console.log(data.status) // ready
}
}

Использование с v-model

Когда вы хотите сделать компонент совместимым с v-model, он должен:

  1. Принимать modelValue как prop.

  2. Генерировать событие update:modelValue.

Дочерний:

<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: \['modelValue'\]
}
</script>

Родитель:

<CustomInput v-model="username" />

В этом случае родитель будет обновлять username автоматически при изменении значения в дочернем компоненте.

Вложенные компоненты и прокси-события

Если компонент A содержит компонент B, а тот — компонент C, и событие генерируется в C, но родителю A нужно его обработать, можно пробросить событие из B:

В компоненте C:

this.$emit('customEvent')

В компоненте B:

<ChildC @customEvent="$emit('customEvent')" />

В компоненте A:

<ChildB @customEvent="handleIt" />

Такое "прокидывание" называется forwarding (перенаправление) событий.

Регистрирование событий в emits

Vue 3 поддерживает декларативную регистрацию возможных событий:

export default {
emits: \['submit', 'close'\]
}

Это позволяет Vue лучше отслеживать корректность использования событий и генерировать предупреждения при ошибках.

Проверка типов и валидаторов для событий

Vue 3 позволяет описывать emits как объект с валидацией аргументов:

emits: {
submit(payload) {
return typeof payload === 'object' && 'name' in payload
}
}

Если проверка не пройдёт — Vue покажет предупреждение в консоли.

События + props = однонаправленный поток

События используются в Vue, чтобы сохранить однонаправленный поток данных:

  • Родитель передаёт данные через props

  • Дочерний компонент вызывает emit, чтобы сообщить об изменении

  • Родитель обрабатывает событие и, при необходимости, обновляет props

Этот паттерн делает взаимодействие между компонентами предсказуемым и чистым.