Во Vue.js взаимодействие между компонентами строится по принципу
однонаправленного потока данных: родитель передаёт данные вниз через
props, а дочерний компонент сообщает о событиях вверх через
механизм событий и эмитов. Такой подход упрощает архитектуру приложения,
делает связи между компонентами явными и контролируемыми.
События используются для передачи факта произошедшего действия, а не данных состояния. Компонент не изменяет данные родителя напрямую, а уведомляет его о необходимости реакции.
$emitДочерний компонент может сгенерировать пользовательское событие,
вызвав метод $emit. Первый аргумент — имя события,
последующие аргументы — произвольные данные.
export default {
methods: {
increment() {
this.$emit('increment', 1)
}
}
}
Здесь компонент сообщает о намерении увеличить значение, не зная, где и как это значение хранится.
Ключевые особенности:
Родительский компонент подписывается на событие через директиву
v-on или сокращённую форму @.
<CounterButton @increment="handleIncrement" />
methods: {
handleIncrement(value) {
this.count += value
}
}
Обработчик получает данные, переданные через $emit, и
принимает решение о дальнейших действиях.
События должны описывать факт, а не действие обработчика. Хорошая практика — использовать глаголы в прошедшем времени или нейтральные формулировки.
Примеры корректных имён:
submitupdate:modelValueitem-selectedНекорректные варианты:
changeParentDatasetValueemits)Во Vue 3 появился явный способ описания событий через опцию
emits. Это повышает читаемость и позволяет фреймворку
выполнять дополнительные проверки.
export default {
emits: ['increment'],
methods: {
increment() {
this.$emit('increment', 1)
}
}
}
Вариант с валидацией аргументов:
export default {
emits: {
increment(value) {
return typeof value === 'number'
}
}
}
Если валидация возвращает false, Vue выдаст
предупреждение в режиме разработки.
update:modelValueМеханизм v-model основан на паре: prop +
событие обновления. По умолчанию используется modelValue и
событие update:modelValue.
<CustomInput v-model="text" />
export default {
props: {
modelValue: String
},
emits: ['update:modelValue'],
methods: {
onInput(event) {
this.$emit('update:modelValue', event.target.value)
}
}
}
Таким образом компонент остаётся управляемым извне, а изменение значения происходит только через событие.
v-modelVue поддерживает несколько v-model с пользовательскими
именами.
<RangeSelector v-model:start="min" v-model:end="max" />
export default {
props: {
start: Number,
end: Number
},
emits: ['update:start', 'update:end']
}
Это позволяет строить сложные формы без нарушения принципов однонаправленного потока данных.
События не всплывают автоматически через несколько уровней вложенности. Если компонент находится глубоко в дереве, возможны варианты:
Ручная прокладка событий
<Child @save="$emit('save')" />
Использование composables или глобального состояния — предпочтительный вариант для сложных сценариев.
Ранее во Vue 2 часто применялась глобальная event bus. Такой подход приводил к неявным зависимостям и трудностям отладки. В современных версиях Vue рекомендуется:
Pinia,
provide/inject или composables.На одно событие можно подписать несколько обработчиков, комбинируя логику:
<MyForm
@submit="validate"
@submit="save"
/>
Порядок вызова соответствует порядку объявления.
Для нативных событий доступны модификаторы (.stop,
.prevent), но для пользовательских событий они не
применяются. Если требуется контролировать поведение, это делается
внутри метода-эмиттера или обработчика.
$emit не поддерживает ожидание результата. Событие —
уведомление, а не запрос. Если требуется вернуть результат:
Promise через prop;Пример с callback:
this.$emit('confirm', () => {
// действие после подтверждения
})
При использовании TypeScript опция emits становится
особенно важной, так как позволяет строго типизировать события и их
аргументы.
emits: {
submit: (payload: { title: string }) => true
}
Это улучшает автодополнение и снижает количество ошибок при разработке.
Чёткое разделение ответственности делает систему компонентов предсказуемой и масштабируемой.