В процессе времени жизни компонента (Lifecycle) можно перехватывать и обрабатывать отдельные события с помощью специальных функций.
Ниже приведен обзор Lifecycle Hooks (хуков жизненного цикла) в Vue.js 3 (с учётом Composition API) в виде таблицы. Для Vue 2 есть небольшие отличия (например, хук beforeUnmount называется beforeDestroy).
Общая схема жизненного цикла компонента Vue:
Сводная таблица хуков жизненного цикла:
Hook (Vue 3)
Аналог в Options API (Vue 2/3)
Когда вызывается
Типичные сценарии использования
onBeforeCreate
beforeCreate
До инициализации компонента: данные, вычисляемые свойства, методы и события ещё не настроены.
Крайне редко, для расширенных плагинов.
onCreated
created
После инициализации реактивных данных, вычисляемых свойств, методов, но до монтирования DOM (Document Object Model).
Запросы к API, инициализация не-DOM данных, работа с событиями.
onBeforeMount
beforeMount
Перед началом монтирования компонента в реальный DOM. Шаблон уже скомпилирован.
Последние операции перед рендерингом (редко).
onMounted
mounted
После монтирования компонента в DOM. Доступен DOM. Дочерние компоненты также смонтированы.
Работа с DOM, инициализация библиотек (карты, графики), таймеры.
onBeforeUpdate
beforeUpdate
При изменении реактивных данных, но до обновления реального DOM.
Получение состояния DOM перед обновлением.
onUpdated
updated
После обновления DOM из-за изменения реактивных данных.
Работа с DOM после обновления, но осторожно — можно вызвать бесконечный цикл.
onBeforeUnmount
beforeDestroy (Vue 2)
Перед началом размонтирования компонента. Компонент всё ещё в рабочем состоянии.
Очистка таймеров, отписка от событий, отмена запросов (prevent memory leaks).
onUnmounted
destroyed (Vue 2)
После полного размонтирования компонента. Все дочерние компоненты размонтированы.
Финальная очистка, уведомление сервера.
onErrorCaptured
errorCaptured
При поимке ошибки из дочернего компонента.
Логирование ошибок, отправка в сервис мониторинга.
onActivated
activated
При активации компонента, сохранённого в KeepAlive.
Повторная подписка на события, обновление данных.
onDeactivated
deactivated
При деактивации компонента, сохранённого в KeepAlive.
Приостановка таймеров, отписка от временных событий.
- Названия хуков размонтирования/уничтожения: в Vue 2 это beforeDestroy и destroyed, в Vue 3 — beforeUnmount и unmounted (что логически точнее).
- Composition API: в Vue 3 хуки нужно явно импортировать и использовать внутри setup() (или < script setup>). В Options API они объявляются как методы компонента.
- Порядок выполнения: общая последовательность (Создание → Монтирование → Обновление → Размонтирование) остаётся неизменной.
Важные предупреждения:
onUpdated: не изменяйте состояние компонента внутри этого хука без условий — это может привести к бесконечному циклу обновлений. onBeforeUpdate и onUpdated: доступ к DOM в этих хуках может быть небезопасным, если изменения не связаны с текущим компонентом.
Для проектов на Vue 2 основные принципы те же, но названия некоторых хуков отличаются. Эта таблица покрывает ключевые моменты жизненного цикла Vue-компонента.
[Примеры onMounted]
Вот несколько практических примеров использования хука onMounted в Vue 3 (Composition API) и, для сравнения, в Vue 2 (Options API).
1️⃣ Пример: Работа с DOM (обращение к элементам)
Самый частый случай — когда нужно получить доступ к элементу DOM после его рендеринга.
// Создаём ref для элемента DOM consttitleElement=ref(null) consttitleText=ref('')
onMounted(()=>{ // Теперь DOM доступен, элемент гарантированно существует if(titleElement.value){ titleText.value=titleElement.value.textContent console.log('DOM элемент заголовка:',titleElement.value)
// Можем изменять DOM напрямую (хотя лучше использовать реактивность) titleElement.value.style.color='blue' }
})
< /script>
constwindowWidth=ref(window.innerWidth) constupdateWidth=()=>{ windowWidth.value=window.innerWidth
}
onMounted(()=>{ // Подписываемся на событие только после монтирования window.addEventListener('resize',updateWidth)
})
// Не забываем отписаться при размонтировании
onUnmounted(()=>{ window.removeEventListener('resize',updateWidth)
})
< /script>
Ключевые моменты использования onMounted:
Ситуация
Рекомендация
Работа с DOM
Используйте ref для получения элементов. onMounted гарантирует, что элемент уже существует в DOM.
Запросы к API
Идеальное место для начальной загрузки данных. Управляйте состояниями загрузки и ошибок.
Сторонние библиотеки
Инициализируйте библиотеки (карты, графики, редакторы) только после рендеринга контейнера.
Таймеры и подписки
Создавайте в onMounted, но обязательно очищайте в onUnmounted (во избежание утечек памяти).
Зависимость от дочерних компонентов
Если нужен доступ к дочерним компонентам, они тоже будут смонтированы к моменту вызова onMounted.
Чего НЕ следует делать в onMounted:
- Не изменяйте реактивные данные синхронно без необходимости — это вызовет лишний ререндер. - Не полагайтесь на onMounted для SSR (серверного рендеринга) — на сервере он не вызывается. - Не забывайте про очистку в onUnmounted для таймеров, подписок и сторонних библиотек.
Альтернатива с watch + ref: иногда вместо onMounted можно использовать watch с опцией flush: 'post' для реакции на изменение DOM:
< scriptsetup> import{watch,ref}from'vue'
constchartContainer=ref(null)
watch(chartContainer,(newElement)=>{ if(newElement){ // Элемент появился в DOM (аналог mounted для этого ref) console.log('Элемент готов:',newElement) }
},{flush:'post'})// 'post' гарантирует выполнение после обновления DOM
< /script>
Эти примеры покрывают основные сценарии использования onMounted. Хук особенно важен для любой работы, требующей доступа к реальному DOM-дереву после полного рендеринга компонента.
[Примеры onUpdated]
Вот практические примеры хука onUpdated в Vue 3 (Composition API) с важными предупреждениями.
Критически важное предупреждение: onUpdated вызывается при КАЖДОМ обновлении DOM компонента. Без контроля это приводит к бесконечным циклам или серьёзным проблемам производительности.
1️⃣ Безопасный пример: отслеживание количества обновлений
// ВАЖНО: Изменение DOM только при необходимости
onUpdated(async()=>{ // Используем nextTick для гарантии, что DOM уже обновлён awaitnextTick()
// Пример: выделение текста в input при определённых условиях if(inputElement.value&&message.value.length>15){ inputElement.value.select() console.log('Текст выделен (длина > 15)') }
// Пример: логирование изменения стилей другого элемента if(textElement.value){ constcolor=message.value.length>10?'darkred':'black' if(textElement.value.style.color!==color){ textElement.value.style.color=color } }
})
< /script>
// Основную логику в watch
watch(data,(newVal)=>{ if(newVal>5){ shouldUpdateDOM.value=true }
})
// onUpdated - только для DOM операций
onUpdated(async()=>{ if(!shouldUpdateDOM.value)return
awaitnextTick()
// Безопасная работа с DOM constelement=document.getElementById('target') if(element){ element.scrollIntoView({behavior:'smooth'}) }
shouldUpdateDOM.value=false// Сбрасываем флаг
})
< /script>
Золотые правила использования onUpdated:
1. Не изменяйте реактивные данные внутри onUpdated. 2. Используйте условия или флаги для ограничения выполнения. 3. Для данных используйте watch, для DOM - onUpdated. 4. Всегда используйте nextTick() если нужен доступ к обновлённому DOM. 5. Помните о производительности - хук вызывается часто.
Альтернатива: watch с flush: post
< scriptsetup> import{watch,ref}from'vue'
constcount=ref(0)
// Выполнится после обновления DOM (как onUpdated, // но для конкретного reactive-источника)
watch(count,()=>{ console.log('Count изменился, DOM обновлён')
},{flush:'post'})
< /script>
Вывод: onUpdated — мощный, но опасный инструмент. В 95% случаев watch или watchEffect с подходящими опциями — лучшее решение. Используйте onUpdated только для операций, требующих гарантированного доступа к уже обновлённому DOM, и всегда с защитными условиями.
[Примеры onUnmounted]
Вот практические примеры использования хука onUnmounted в Vue 3 (Composition API). Этот хук критически важен для предотвращения утечек памяти.
// 3. Удаляем все обработчики событий eventListeners.forEach(({element,type,handler})=>{ element.removeEventListener(type,handler) }) eventListeners.length=0console.log('Обработчики событий удалены')
// Регистрируем очистки
registerCleanup(()=>{ console.log('Первая задача очистки')
})
registerCleanup(()=>{ console.log('Вторая задача очистки')
})
// Выполняем все очистки
onUnmounted(()=>{ cleanupCallbacks.forEach(callback=>callback()) cleanupCallbacks.length=0
})
< /script>
Распространённые ошибки
< scriptsetup> // ❌ НЕДОСТАТОЧНАЯ очистка lettimer=setInterval(()=>{},1000)
onUnmounted(()=>{ clearInterval(timer)// ОК // Но timer всё ещё содержит ID, лучше обнулить
})
1. Каждому onMounted — свой onUnmounted. 2. Очищайте всё, что создаётся вручную. 3. Используйте AbortController для fetch/axios. 4. Обнуляйте ссылки после очистки. 5. Тестируйте утечки памяти (Chrome DevTools → Memory).
Правильная очистка в onUnmounted — признак качественного Vue-кода и защита от "зомби-приложений", которые продолжают работать после удаления компонентов.
[watсh: отличие от onUpdated]
watch и onUpdated решают разные задачи, хотя оба реагируют на изменения. Вот ключевые отличия:
Критерий
watch / watchEffect
onUpdated
Основное назначение
Реагировать на изменения конкретных данных (реактивных состояний, props [2]).
Выполнить код после любого обновления DOM компонента.
Когда вызывается
Сразу после изменения отслеживаемых реактивных данных (до обновления DOM, если не указано flush: 'post').
После каждого обновления виртуального DOM и применения изменений к реальному DOM.
Доступ к DOM
По умолчанию нет доступа (DOM может быть устаревшим). С flush: 'post' — есть, но это специализированный случай.
Есть (компонент только что обновился).
Производительность
Высокая, так как можно точно указать, какие данные отслеживать.
Потенциально низкая, хук срабатывает при любом обновлении, без фильтрации.
Риск бесконечного цикла
Низкий (если не изменять внутри отслеживаемые данные без условий).
Очень высокий, если внутри хука изменить реактивное состояние.
Типичный сценарий
Загрузка новых данных при изменении ID, валидация, вычисление производных значений.
Интеграция с библиотекой, требующей работы с DOM после обновлений, скроллинг к элементу.
Аргументы watch. По умолчанию функция watch получает значение отслеживаемой переменной уже после того, как она была изменена, через первый параметр (в этом примере отслеживаемая переменная userId):
Набор параметров третьего аргумента watch. Также можно предоставить для watch третий аргумент, который представляет собой набор специальных параметров. Один из них это boolean-флажок immediate. Если он установлен в true, то это заставляет вызвать watch, когда компонент запускается:
Функция watchEffect не принимает никаких агументов зависимостей, и принимает сразу только callback-функцию. Причина в том, что любое реактивное состояние переменной, которое встретилось в теле этой callback-функции, автоматически становится зависимостью.
Функция watchEffect позволяет сильно сократить код, когда имеется несколько ослеживаемых зависимостей.
Когда что использовать. Используйте watch (в 90% случаев):
- Реакция на изменение данных (запрос к API при смене фильтра). - Валидация или вычисление производных значений. - Выполнение логики при изменении конкретного пропса или состояния.
Используйте onUpdated с осторожностью (в 10% случаев):
- Для отладки (логирование после каждого рендера). - Для работы со сторонними библиотеками, которым нужен доступ к обновлённому DOM. - Когда нужно синхронизировать не-Vue состояние с DOM.
Практические примеры
Пример 1: Загрузка данных при изменении ID (идеально для watch)
< scriptsetup> import{ref,watch}from'vue'
constuserId=ref(1) constuserData=ref(null)
// watch СФОКУСИРОВАН на изменении конкретного reactive-источника
watch(userId,async(newId)=>{ constresponse=awaitfetch(`/api/users/${newId}`) userData.value=awaitresponse.json()
},{immediate:true})// immediate:true выполнит запрос сразу при создании
< /script>
Пример 2: Работа с DOM после обновления (случай для onUpdated)
// onUpdated отслеживает ЛЮБОЕ обновление компонента
onUpdated(async()=>{ // Ждём следующего тика, чтобы быть уверенным в обновлении DOM awaitnextTick()
// Прокручиваем список к низу при добавлении новых сообщений if(messageList.value){ messageList.value.scrollTop=messageList.value.scrollHeight }
})
< /script>
Пример 3: антипаттерн — НЕ используйте onUpdated там, где нужен watch
// ❌ ПЛОХО: onUpdated сработает при ЛЮБОМ обновлении, // не только при изменении searchQuery
onUpdated(async()=>{ constresponse=awaitfetch(`/api/search?q=${searchQuery.value}`) searchResults.value=awaitresponse.json() // Беда! Запрос будет выполняться при каждом рендере, // вызывая бесконечные запросы, если ответ меняет // какие-то реактивные данные.
})
// ✅ ХОРОШО: watch сработает ТОЛЬКО при изменении searchQuery
watch(searchQuery,async(newQuery)=>{ constresponse=awaitfetch(`/api/search?q=${newQuery}`) searchResults.value=awaitresponse.json()
})
< /script>
Специальный случай: watch с flush: 'post'. Иногда нужна логика watch, но после обновления DOM. Для этого есть опция flush: 'post':
< scriptsetup> import{ref,watch}from'vue'
constitems=ref([]) constlistElement=ref(null)
// watch с flush: 'post' сработает ПОСЛЕ обновления DOM
watch(items,()=>{ // Теперь можно безопасно работать с DOM if(listElement.value){ console.log('Высота списка после обновления:',listElement.value.clientHeight) }
},{flush:'post'})// < -- Ключевая опция
< /script>
Итог, простое правило:
watch — следит за данными и реагирует на их изменение. onUpdated — следит за компонентом и реагирует на его перерисовку.
Практическая рекомендация: если не работаете напрямую с обновлённым DOM, почти всегда выбирайте watch. Он даёт больше контроля, эффективнее и безопаснее. onUpdated — это "тяжёлая артиллерия" для специфических DOM-задач.
[onWatcherCleanup: использование для watch и watchEffect]
onCleanup (также называемый эффектом очистки) — мощный механизм внутри watch и watchEffect для управления побочными эффектами.
Основное назначение. Функция onCleanup регистрирует код, который выполнится перед следующим вызовом эффекта или при остановке вотчера. Это аналог onUnmounted, но для отдельных эффектов.
// Отслеживаем изменения данных для обновления графика
watch(chartData,(newData,oldData,onCleanup)=>{ if(!chartInstance){ chartInstance=initChartLibrary('#chart-container') }
// Подписываемся на событие клика по графику constclickHandler=(event)=>{ console.log('Клик по точке:',event.point) }
chartInstance.on('click',clickHandler)
// Отписываемся перед следующим обновлением onCleanup(()=>{ chartInstance.off('click',clickHandler) }) chartInstance.updateData(newData)
},{deep:true})
< /script>
// ❌ ОШИБКА: onCleanup вызывается асинхронно
watchEffect(async(onCleanup)=>{ constdata=awaitfetchData() // Уже поздно! Эффект мог перезапуститься до этого момента onCleanup(()=>{/* ... */})// Не сработает как ожидается
})
2. Несколько вызовов onCleanup
watchEffect((onCleanup)=>{ // Можно регистрировать несколько функций очистки onCleanup(()=>console.log('Очистка 1')) onCleanup(()=>console.log('Очистка 2'))
// Они выполнятся в порядке, обратном регистрации: // 1. "Очистка 2" // 2. "Очистка 1"
})
1. Эффект создаёт ресурсы, которые живут только до следующего вызова. 2. Нужно отменять предыдущие операции (запросы, таймеры). 3. Подписки/отписки на события, связанные с конкретными данными. 4. Временная интеграция со сторонними библиотеками.
Ключевое преимущество: onCleanup инкапсулирует логику очистки внутри самого эффекта, делая код более модульным и предотвращая утечки памяти.