Для обработки DOM-событий в Vue используется директива v-on или её сокращение @:
<template>
<!-- Полная форма -->
<button v-on:click="handleClick">Нажми</button>
<!-- Сокращённая форма (используется чаще) -->
<button @click="handleClick">Нажми</button>
<!-- Передача аргументов -->
<button @click="greet('Алексей')">Поздороваться</button>
<!-- Доступ к объекту события -->
<button @click="handleClick($event)">С событием</button>
</template>
<script setup>
function handleClick(event) {
console.log('Клик!', event.target)
}
function greet(name) {
console.log(`Привет, ${name}!`)
}
</script><!-- Inline — для простой логики -->
<button @click="count++">+1</button>
<button @click="items.push(newItem)">Добавить</button>
<!-- Method — для сложной логики -->
<button @click="submitForm">Отправить</button>Vue предоставляет **модификаторы** — удобные суффиксы для распространённых операций:
<!-- .prevent — вызывает event.preventDefault() -->
<form @submit.prevent="handleSubmit">...</form>
<a @click.prevent="navigate" href="/page">Ссылка</a>
<!-- .stop — вызывает event.stopPropagation() -->
<div @click="outerClick">
<button @click.stop="innerClick">Клик не всплывает</button>
</div>
<!-- .once — обработчик сработает только один раз -->
<button @click.once="sendAnalytics">Первый клик</button>
<!-- .self — только если клик именно на элемент (не на дочерний) -->
<div @click.self="handleSelfClick">
<span>Клик на span не вызовет handleSelfClick</span>
</div>
<!-- Можно цеплять несколько модификаторов -->
<form @submit.prevent.stop="handleSubmit">...</form><!-- Только при нажатии Enter -->
<input @keyup.enter="submit">
<!-- Только при нажатии Escape -->
<input @keyup.esc="cancel">
<!-- Ctrl + S -->
<input @keyup.ctrl.s="save">
<!-- Любая клавиша-стрелка -->
<div @keydown.arrow-up="moveUp">
<div @keydown.arrow-down="moveDown">Компонент может генерировать собственные события через emit:
<!-- ChildComponent.vue -->
<template>
<button @click="handleClick">Отправить данные родителю</button>
</template>
<script setup>
const emit = defineEmits(['data-sent', 'error'])
function handleClick() {
emit('data-sent', { value: 42, timestamp: Date.now() })
}
</script><!-- ParentComponent.vue -->
<template>
<ChildComponent @data-sent="handleData" @error="handleError" />
</template>
<script setup>
function handleData(payload) {
console.log('Получено от ребёнка:', payload)
}
</script>Система событий Vue (и Node.js, и браузерного DOM) основана на паттерне **EventEmitter** (или Pub/Sub). Это позволяет компонентам общаться без прямых ссылок друг на друга:
emit) — генерирует событияon) — слушает событияoff) — прекращает слушатьonce) — слушает ровно один разEventEmitter — паттерн событий, лежащий в основе системы событий Vue
// EventEmitter — фундаментальный паттерн для работы с событиями.
// Vue использует его для компонентных событий, Node.js — для I/O,
// браузер — для DOM-событий.
class EventEmitter {
constructor() {
// Map: имя события -> массив обработчиков
this._events = new Map()
}
// Подписаться на событие
on(event, handler) {
if (!this._events.has(event)) {
this._events.set(event, [])
}
this._events.get(event).push(handler)
console.log(`[on] Подписан на "${event}"`)
return this // для цепочки вызовов
}
// Отписаться от события
off(event, handler) {
if (!this._events.has(event)) return this
const handlers = this._events.get(event)
const index = handlers.indexOf(handler)
if (index > -1) {
handlers.splice(index, 1)
console.log(`[off] Отписан от "${event}"`)
}
return this
}
// Генерировать событие
emit(event, ...args) {
if (!this._events.has(event)) return this
const handlers = this._events.get(event)
console.log(`[emit] "${event}" -> ${handlers.length} обработчиков`)
handlers.forEach(handler => handler(...args))
return this
}
// Подписаться один раз (автоматическая отписка после первого вызова)
once(event, handler) {
const wrapper = (...args) => {
handler(...args)
this.off(event, wrapper) // отписываемся сразу после вызова
}
return this.on(event, wrapper)
}
}
// --- Демонстрация ---
const emitter = new EventEmitter()
function onData(data) {
console.log('Получены данные:', data)
}
function onDataExtra(data) {
console.log('Дополнительный обработчик:', data)
}
// Подписываемся
emitter.on('data', onData)
emitter.on('data', onDataExtra)
// Одноразовый обработчик
emitter.once('connect', () => {
console.log('Подключение установлено (сработает только один раз)')
})
console.log('\n--- Первый emit ---')
emitter.emit('data', { value: 42 })
console.log('\n--- connect: первый раз ---')
emitter.emit('connect')
console.log('\n--- connect: второй раз (once уже отписан) ---')
emitter.emit('connect')
console.log('\n--- Отписываем onDataExtra ---')
emitter.off('data', onDataExtra)
emitter.emit('data', { value: 100 }) // только onDataДля обработки DOM-событий в Vue используется директива v-on или её сокращение @:
<template>
<!-- Полная форма -->
<button v-on:click="handleClick">Нажми</button>
<!-- Сокращённая форма (используется чаще) -->
<button @click="handleClick">Нажми</button>
<!-- Передача аргументов -->
<button @click="greet('Алексей')">Поздороваться</button>
<!-- Доступ к объекту события -->
<button @click="handleClick($event)">С событием</button>
</template>
<script setup>
function handleClick(event) {
console.log('Клик!', event.target)
}
function greet(name) {
console.log(`Привет, ${name}!`)
}
</script><!-- Inline — для простой логики -->
<button @click="count++">+1</button>
<button @click="items.push(newItem)">Добавить</button>
<!-- Method — для сложной логики -->
<button @click="submitForm">Отправить</button>Vue предоставляет **модификаторы** — удобные суффиксы для распространённых операций:
<!-- .prevent — вызывает event.preventDefault() -->
<form @submit.prevent="handleSubmit">...</form>
<a @click.prevent="navigate" href="/page">Ссылка</a>
<!-- .stop — вызывает event.stopPropagation() -->
<div @click="outerClick">
<button @click.stop="innerClick">Клик не всплывает</button>
</div>
<!-- .once — обработчик сработает только один раз -->
<button @click.once="sendAnalytics">Первый клик</button>
<!-- .self — только если клик именно на элемент (не на дочерний) -->
<div @click.self="handleSelfClick">
<span>Клик на span не вызовет handleSelfClick</span>
</div>
<!-- Можно цеплять несколько модификаторов -->
<form @submit.prevent.stop="handleSubmit">...</form><!-- Только при нажатии Enter -->
<input @keyup.enter="submit">
<!-- Только при нажатии Escape -->
<input @keyup.esc="cancel">
<!-- Ctrl + S -->
<input @keyup.ctrl.s="save">
<!-- Любая клавиша-стрелка -->
<div @keydown.arrow-up="moveUp">
<div @keydown.arrow-down="moveDown">Компонент может генерировать собственные события через emit:
<!-- ChildComponent.vue -->
<template>
<button @click="handleClick">Отправить данные родителю</button>
</template>
<script setup>
const emit = defineEmits(['data-sent', 'error'])
function handleClick() {
emit('data-sent', { value: 42, timestamp: Date.now() })
}
</script><!-- ParentComponent.vue -->
<template>
<ChildComponent @data-sent="handleData" @error="handleError" />
</template>
<script setup>
function handleData(payload) {
console.log('Получено от ребёнка:', payload)
}
</script>Система событий Vue (и Node.js, и браузерного DOM) основана на паттерне **EventEmitter** (или Pub/Sub). Это позволяет компонентам общаться без прямых ссылок друг на друга:
emit) — генерирует событияon) — слушает событияoff) — прекращает слушатьonce) — слушает ровно один разEventEmitter — паттерн событий, лежащий в основе системы событий Vue
// EventEmitter — фундаментальный паттерн для работы с событиями.
// Vue использует его для компонентных событий, Node.js — для I/O,
// браузер — для DOM-событий.
class EventEmitter {
constructor() {
// Map: имя события -> массив обработчиков
this._events = new Map()
}
// Подписаться на событие
on(event, handler) {
if (!this._events.has(event)) {
this._events.set(event, [])
}
this._events.get(event).push(handler)
console.log(`[on] Подписан на "${event}"`)
return this // для цепочки вызовов
}
// Отписаться от события
off(event, handler) {
if (!this._events.has(event)) return this
const handlers = this._events.get(event)
const index = handlers.indexOf(handler)
if (index > -1) {
handlers.splice(index, 1)
console.log(`[off] Отписан от "${event}"`)
}
return this
}
// Генерировать событие
emit(event, ...args) {
if (!this._events.has(event)) return this
const handlers = this._events.get(event)
console.log(`[emit] "${event}" -> ${handlers.length} обработчиков`)
handlers.forEach(handler => handler(...args))
return this
}
// Подписаться один раз (автоматическая отписка после первого вызова)
once(event, handler) {
const wrapper = (...args) => {
handler(...args)
this.off(event, wrapper) // отписываемся сразу после вызова
}
return this.on(event, wrapper)
}
}
// --- Демонстрация ---
const emitter = new EventEmitter()
function onData(data) {
console.log('Получены данные:', data)
}
function onDataExtra(data) {
console.log('Дополнительный обработчик:', data)
}
// Подписываемся
emitter.on('data', onData)
emitter.on('data', onDataExtra)
// Одноразовый обработчик
emitter.once('connect', () => {
console.log('Подключение установлено (сработает только один раз)')
})
console.log('\n--- Первый emit ---')
emitter.emit('data', { value: 42 })
console.log('\n--- connect: первый раз ---')
emitter.emit('connect')
console.log('\n--- connect: второй раз (once уже отписан) ---')
emitter.emit('connect')
console.log('\n--- Отписываем onDataExtra ---')
emitter.off('data', onDataExtra)
emitter.emit('data', { value: 100 }) // только onDataРеализуй класс `EventEmitter` с методами: `on(event, handler)` — подписаться, `off(event, handler)` — отписаться, `emit(event, ...args)` — вызвать все обработчики события, `once(event, handler)` — подписаться только на один вызов (после первого вызова автоматически отписывается). Все методы должны возвращать `this` для возможности цепочки вызовов.
В once создай const wrapper = (...args) => { handler(...args); this.off(event, wrapper); }, затем верни this.on(event, wrapper). Важно: сохрани wrapper в переменную ДО вызова on, иначе off не найдёт нужную функцию. В off используй: const handlers = this._events.get(event); const i = handlers.indexOf(handler); if (i > -1) handlers.splice(i, 1).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке