Современные фронтенд-приложения строятся из компонентов, которые должны взаимодействовать между собой. Во фреймворке это взаимодействие реализовано через строгую модель однонаправленного потока данных: сверху вниз передаются данные, а снизу вверх — события.
Ключевым инструментом для передачи событий от дочернего компонента к родительскому является emit. Понимание этого механизма — важный шаг к созданию масштабируемых и поддерживаемых приложений. В этой статье разберём, как работает emit, чем он отличается от props, как его правильно использовать и какие ошибки встречаются чаще всего. Подробнее на https://frontendbase.ru/vue/vue-emit/.
Что такое emit и зачем он нужен
В архитектуре Vue компоненты изолированы друг от друга. Родитель может передавать данные дочернему компоненту через props, но сам дочерний компонент не может напрямую изменить состояние родителя.
Именно здесь появляется emit.
Emit — это механизм, с помощью которого дочерний компонент отправляет событие родителю.
Простое объяснение:
- props — это «вход» (данные приходят в компонент),
- emit — это «выход» (компонент сообщает о событии наружу).
Пример жизненного сценария:
Пользователь нажимает кнопку «Сохранить» в дочернем компоненте. Сам компонент не знает, что делать дальше — сохранить данные, отправить на сервер или показать уведомление. Он просто вызывает событие через emit, а родитель решает, как его обработать.
Такой подход:
- снижает связанность компонентов,
- упрощает повторное использование,
- делает код предсказуемым.
Разница между props и emit
Props и emit — это две стороны одной архитектурной модели.
| Характеристика | Props | Emit |
|---|---|---|
| Направление | Сверху вниз | Снизу вверх |
| Назначение | Передача данных | Передача событий |
| Кто инициирует | Родитель | Дочерний компонент |
| Можно ли изменять | Нет | Да (вызывается как функция) |
Важный принцип:
Дочерний компонент не должен изменять props напрямую. Вместо этого он должен:
- Сгенерировать событие через emit
- Передать данные родителю
- Дождаться обновления через props
Это называется однонаправленный поток данных — ключевая концепция Vue.
Базовое использование emit в <script setup>
В Vue 3 с использованием Composition API события объявляются через defineEmits.
Пример:
const emit = defineEmits([‘submit’, ‘cancel’])function handleClick() {
emit(‘submit’, { id: 1, value: ‘test’ })
}
</script>
<template>
<button @click=«handleClick«>Отправить</button>
</template>
Как это работает:
defineEmitsобъявляет список событий;- функция
emitвызывает событие; - данные передаются как аргумент.
Подписка в родителе:
function onSubmit(payload) {
console.log(payload)
}
</script>
Особенность именования:
- в коде:
emit('myEvent') - в шаблоне:
@my-event
Vue автоматически приводит регистр.
Использование emit без <script setup>
До появления нового синтаксиса использовались другие подходы.
Options API:
emits: [‘update’],
methods: {
sendUpdate() {
this.$emit(‘update’, 42)
}
}
}
Composition API:
emits: [‘update’],
setup(props, ctx) {
function sendUpdate() {
ctx.emit(‘update’, 42)
}
return { sendUpdate }
}
}
Оба варианта актуальны, но <script setup> считается более современным и удобным.
Валидация событий и данных
Vue позволяет проверять корректность данных (payload), передаваемых через emit.
Пример с валидатором:
const emit = defineEmits({
submit: (payload) => {
return payload && typeof payload.value === ‘string’
},
cancel: null
})
</script>
Что это даёт:
- контроль структуры данных,
- предупреждения в консоли при ошибках,
- более надёжный код.
⚠️ Важно: валидация работает только в режиме разработки и не влияет на production.
Типизация emit в TypeScript
В проектах с TypeScript emit можно строго типизировать.
Пример:
interface UserPayload {
id: number
name: string
}const emit = defineEmits<{
(e: ‘save’, user: UserPayload): void
(e: ‘cancel’): void
}>()
function handleSave() {
emit(‘save’, { id: 1, name: ‘Alex’ })
}
</script>
Преимущества:
- автодополнение в IDE,
- защита от ошибок типов,
- проверка имён событий.
Например, emit('sav') вызовет ошибку ещё на этапе компиляции.
Emit и работа с v-model
В Vue v-model — это удобный способ двустороннего связывания, который основан на emit.
Как это работает:
- prop:
modelValue - событие:
update:modelValue
Пример:
defineProps<{ modelValue: string }>()const emit = defineEmits<{
(e: ‘update:modelValue’, value: string): void
}>()
function onInput(event) {
emit(‘update:modelValue’, event.target.value)
}
</script>
<template>
<input :value=«modelValue« @input=«onInput« />
</template>
Использование:
Можно создавать несколько моделей:
v-model:titlev-model:description
Это удобно для сложных компонентов.
Частые ошибки при работе с emit
Даже опытные разработчики иногда допускают типичные ошибки.
1. Изменение props напрямую
❌ Нарушает архитектуру
✅ Нужно использовать emit
2. Необъявленные события
Если событие не указано в defineEmits:
- Vue выдаст предупреждение,
- событие может вести себя как обычное DOM-событие.
3. Использование emit вне контекста
defineEmits работает только внутри <script setup>.
Если логика вынесена в composable — emit нужно передать вручную.
4. Ожидание валидации в production
Валидаторы не работают в продакшене — это только инструмент разработки.
Практические рекомендации
Чтобы использовать emit эффективно:
- всегда объявляйте события явно;
- используйте понятные имена (
submit,update,close); - типизируйте события в TypeScript;
- не смешивайте бизнес-логику с UI;
- проверяйте данные перед отправкой.
Механизм emit — это фундаментальная часть архитектуры Vue, обеспечивающая взаимодействие компонентов при сохранении их независимости. Он реализует передачу событий снизу вверх, дополняя поток данных через props.
Грамотное использование emit позволяет:
- создавать переиспользуемые компоненты,
- улучшать читаемость кода,
- избегать ошибок при масштабировании проекта.
Современные инструменты Vue 3, такие как defineEmits и поддержка TypeScript, делают работу с событиями более удобной и безопасной. Освоив этот механизм, разработчик получает мощный инструмент для построения гибких и устойчивых интерфейсов.
