Олег Марков
Хуки жизненного цикла в Vue 3 Composition API - полный разбор с примерами
Введение
Хуки жизненного цикла в Composition API — это фундаментальный механизм, который позволяет вам "подцепиться" к разным этапам жизни компонента Vue 3: созданию, монтированию, обновлению, уничтожению и активации или деактивации (для keep-alive). Без понимания lifecycle-hooks сложно строить предсказуемые и устойчивые к ошибкам интерфейсы.
В этой статье вы разберете, как именно работают lifecycle-хуки в Composition API, чем они отличаются от опций created, mounted и других в Options API, как правильно организовывать код и как избегать частых ошибок (например, утечек памяти из-за неочищенных подписок и обработчиков событий).
Давайте шаг за шагом посмотрим, как устроен жизненный цикл компонента и как им управлять с помощью Composition API.
Обзор жизненного цикла в Vue 3 и место Composition API
Кратко о жизненном цикле компонента
У компонента Vue есть несколько ключевых этапов:
- Инициализация
- Монтирование в DOM
- Обновление при изменении состояния или пропсов
- Демонтирование из DOM
- (Опционально) Активация и деактивация при использовании keep-alive
На каждом этапе вы можете выполнить свой код. Раньше это делалось через методы Options API (beforeCreate, created, mounted, beforeUnmount и так далее). В Composition API используются функции-хуки, которые вызываются внутри setup.
Отличие lifecycle-хуков в Composition API от Options API
Основные отличия:
- Вместо свойств объекта компонента (
mounted() { ... }) вы используете импортируемые функции (onMounted(() => { ... })). - Вы можете вызывать один и тот же хук несколько раз в одном компоненте — все обработчики отработают.
- Логика становится проще для переиспользования: выносите её в отдельные функции (composition functions), а хуки вызываете внутри этих функций.
Смотрите, как это выглядит в коде.
Пример: Options API vs Composition API
Options API:
// Компонент в стиле Options API
export default {
data() {
return {
count: 0, // Состояние компонента
}
},
mounted() {
// Выполнится, когда компонент будет смонтирован в DOM
console.log('Компонент смонтирован')
},
beforeUnmount() {
// Выполнится перед удалением компонента из DOM
console.log('Компонент скоро будет удален')
},
}
Composition API:
// Компонент в стиле Composition API
import { ref, onMounted, onBeforeUnmount } from 'vue'
export default {
setup() {
const count = ref(0) // Состояние компонента
onMounted(() => {
// Выполнится, когда компонент будет смонтирован в DOM
console.log('Компонент смонтирован')
})
onBeforeUnmount(() => {
// Выполнится перед удалением компонента из DOM
console.log('Компонент скоро будет удален')
})
return {
count, // Экспортируем реактивное состояние в шаблон
}
},
}
Как видите, lifecycle-функции вызываются внутри setup и принимают колбэк, который выполнится в нужный момент.
Список lifecycle-хуков в Composition API
В Composition API доступны следующие хуки жизненного цикла:
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
- onErrorCaptured
- onRenderTracked
- onRenderTriggered
- onActivated
- onDeactivated
- onServerPrefetch (для SSR)
Давайте разберем основные из них по группам, с примерами и типичными сценариями применения.
Инициализация и монтирование компонента
onBeforeMount
onBeforeMount вызывается один раз перед тем, как компонент будет смонтирован в DOM. На этом этапе у вас есть уже инициализированное состояние, но еще нет реального DOM-дерева компонента.
import { ref, onBeforeMount, onMounted } from 'vue'
export default {
setup() {
const message = ref('')
onBeforeMount(() => {
// Здесь мы можем подготовить данные до появления компонента в DOM
// Например, задать значение по умолчанию
message.value = 'Подготовка перед монтированием'
console.log('onBeforeMount - DOM еще не доступен')
})
onMounted(() => {
// Здесь DOM уже доступен
console.log('onMounted - компонент в DOM')
})
return {
message,
}
},
}
Где это полезно:
- Логирование этапов инициализации.
- Подготовка состояния, которое не зависит от DOM.
- Редко используется для сложной логики, так как часто хватает
setupиonMounted.
onMounted
onMounted — один из самых часто используемых хуков. Он вызывается, когда компонент уже вставлен в DOM, и вы можете обращаться к реальным DOM-элементам (например, через ref в шаблоне) или вызывать библиотеки, которые зависят от DOM (например, инициализация графиков или сторонних виджетов).
Давайте разберемся на примере.
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null) // Ссылка на DOM-элемент
onMounted(() => {
// Здесь DOM уже построен, и inputRef.value ссылается на реальный <input>
if (inputRef.value) {
// Устанавливаем фокус на поле ввода после монтирования
inputRef.value.focus()
}
// Здесь же можно вызвать запросы к API, если они нужны сразу после отображения
console.log('Компонент смонтирован и видим пользователю')
})
</script>
<template>
<input ref="inputRef" />
</template>
Комментарии:
inputRefсвязывается с элементом<input ref="inputRef" />после монтирования.- Внутри
onMountedмы уверены, чтоinputRef.value— это реальный DOM-элемент.
Где использовать onMounted:
- Инициализация сторонних JS-библиотек.
- Фокус и скролл.
- Старт подписок, если они завязаны на DOM.
- Запросы к API, если данные нужны только после появления компонента (иногда это делают и в
setup, но это уже вопрос архитектуры).
Обновление компонента
onBeforeUpdate и onUpdated
Эти хуки вызываются при каждом обновлении компонента из-за изменения реактивных данных или пропсов.
onBeforeUpdate— перед обновлением DOM.onUpdated— после того как DOM обновился.
Теперь давайте посмотрим, что происходит в примере.
<script setup>
import { ref, onBeforeUpdate, onUpdated } from 'vue'
const count = ref(0)
const lastDomText = ref('') // Храним последнее значение текста в DOM
onBeforeUpdate(() => {
// Здесь DOM отображает старое состояние
// Мы можем зафиксировать, что было до обновления
const el = document.getElementById('counter')
if (el) {
// Сохраняем старый текст перед обновлением
lastDomText.value = el.textContent
console.log('Перед обновлением DOM - текст был:', lastDomText.value)
}
})
onUpdated(() => {
// Здесь DOM уже обновлен
const el = document.getElementById('counter')
if (el) {
console.log('После обновления DOM - текст стал:', el.textContent)
}
})
const increment = () => {
// Увеличиваем значение счетчика
count.value++
}
</script>
<template>
<div>
<p id="counter">Текущее значение - {{ count }}</p>
<button @click="increment">Увеличить</button>
</div>
</template>
Для чего использовать:
- Интеграция с библиотеками, которые напрямую изменяют DOM (и вам нужно их синхронизировать с Vue).
- Сложный анализ производительности и логирование изменений.
- Аккуратные манипуляции с DOM, когда нельзя сделать это декларативно.
Важно: для большинства задач лучше использовать реактивность и вычисляемые свойства, чем напрямую полагаться на onUpdated.
Демонтирование компонента и очистка ресурсов
onBeforeUnmount и onUnmounted
Когда компонент перестает быть нужным (его удаляют из DOM), вы должны аккуратно освободить ресурсы:
- отписаться от событий (window, document, WebSocket),
- остановить таймеры и интервалы,
- отменить подписки на сторонние источники.
Если этого не сделать, можно получить утечки памяти или странное поведение, когда обработчики продолжают срабатывать после ухода компонента.
onBeforeUnmount вызывается перед удалением компонента, а onUnmounted — сразу после удаления из DOM.
Покажу вам, как это реализовано на практике.
import { ref, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
export default {
setup() {
const width = ref(window.innerWidth)
const handleResize = () => {
// Обработчик события изменения размера окна
width.value = window.innerWidth
}
onMounted(() => {
// Подписываемся на событие resize после монтирования компонента
window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
// Важно - отписаться от события до удаления компонента
window.removeEventListener('resize', handleResize)
console.log('Компонент будет демонтирован - обработчик снят')
})
onUnmounted(() => {
// Здесь можно провести финальное логирование или очистку
console.log('Компонент демонтирован окончательно')
})
return {
width,
}
},
}
На что обратить внимание:
- Никогда не оставляйте подписки "висячими".
- Лучше всего, когда весь код подписки и отписки собран в одном месте, а lifecycle-хуки отвечают за начало и конец жизненного цикла этой логики.
Если вы выносите подобную логику в композиционную функцию, подписки и отписки по-прежнему должны быть привязаны к хукам текущего компонента — ниже вы увидите, как это делается.
Работа с keep-alive: onActivated и onDeactivated
Если вы используете <keep-alive> для кеширования компонентов (обычно это страницы или вкладки), то компоненты не уничтожаются при "уходе" со страницы, а только деактивируются. При возврате они "активируются" снова.
В Options API для этого есть хуки activated и deactivated. В Composition API используются onActivated и onDeactivated.
Давайте посмотрим, что происходит в следующем примере.
<template>
<keep-alive>
<!-- Активный компонент будет сохранять состояние между переключениями -->
<UserList v-if="currentTab === 'users'" />
<UserProfile v-else-if="currentTab === 'profile'" />
</keep-alive>
<button @click="currentTab = 'users'">Пользователи</button>
<button @click="currentTab = 'profile'">Профиль</button>
</template>
<script setup>
import { ref } from 'vue'
import UserList from './UserList.vue'
import UserProfile from './UserProfile.vue'
const currentTab = ref('users') // Текущая вкладка
</script>
Теперь внутри UserList воспользуемся хуками:
// UserList.vue
import { ref, onMounted, onActivated, onDeactivated } from 'vue'
export default {
setup() {
const items = ref([])
onMounted(() => {
// Здесь получаем данные один раз при первом монтировании
console.log('UserList смонтирован')
// Здесь могла бы быть загрузка данных
})
onActivated(() => {
// Вызывается каждый раз при повторной активации из кеша
console.log('UserList активирован - компонент снова видим')
// Можно обновить данные или восстановить состояние
})
onDeactivated(() => {
// Вызывается, когда компонент "прячут" в keep-alive
console.log('UserList деактивирован - компонент скрыт, но не уничтожен')
// Здесь можно, например, остановить автообновление данных
})
return {
items,
}
},
}
Здесь важно понимать:
onMountedвызывается только при первом создании компонента.onUnmountedвызывается только при полном уничтожении кеша.onActivatedиonDeactivatedмогут вызываться много раз при переключении вкладок.
Обработка ошибок: onErrorCaptured
Vue 3 позволяет перехватывать ошибки, которые возникают в дочерних компонентах. В Options API для этого есть errorCaptured. В Composition API используется onErrorCaptured.
Это полезно, когда вам нужно локально обработать ошибку (показать fallback-контент, отправить лог, отменить какую-то операцию), но не "ронять" все приложение.
Давайте разберемся на примере.
import { ref, onErrorCaptured } from 'vue'
export default {
setup() {
const hasError = ref(false)
const errorMessage = ref('')
onErrorCaptured((err, instance, info) => {
// err - сама ошибка
// instance - экземпляр компонента, где ошибка произошла
// info - строка с информацией, в каком хуке или рендере она произошла
// Фиксируем, что произошла ошибка
hasError.value = true
errorMessage.value = String(err)
// Здесь мы можем отправить ошибку на сервер логирования
// sendToErrorTracker(err, info)
// Если вернуть false - ошибка продолжит всплытие выше по дереву
// Если вернуть true или ничего не возвращать - всплытие будет остановлено
return false
})
return {
hasError,
errorMessage,
}
},
}
Когда использовать:
- Локальный error boundary вокруг "опасной" части UI.
- Логирование ошибок конкретных участков интерфейса.
- Показ fallback-контента, если компонент рендерится с ошибкой.
Диагностика рендера: onRenderTracked и onRenderTriggered
Эти два хука рассчитаны в первую очередь на отладку и анализ производительности.
onRenderTrackedвызывается каждый раз, когда реактивное свойство отслеживается во время рендера.onRenderTriggeredвызывается, когда реактивное изменение запускает повторный рендер.
Давайте посмотрим, как это выглядит в простом примере.
import { ref, onRenderTracked, onRenderTriggered } from 'vue'
export default {
setup() {
const count = ref(0)
const text = ref('hello')
onRenderTracked((event) => {
// Срабатывает, когда рендер "подписывается" на реактивное свойство
console.log('Отслежено свойство в рендере', {
type: event.type, // Например get
target: event.target, // Объект, в котором находится свойство
key: event.key, // Имя свойства, например 'count'
})
})
onRenderTriggered((event) => {
// Срабатывает, когда изменение свойства инициирует повторный рендер
console.log('Рендер запущен из-за изменения', {
type: event.type, // Например set
target: event.target,
key: event.key,
newValue: event.newValue,
oldValue: event.oldValue,
})
})
const increment = () => {
// Изменение count приведет к перерендеру, если count используется в шаблоне
count.value++
}
return {
count,
text,
increment,
}
},
}
Обратите внимание:
- Эти хуки не предназначены для бизнес-логики.
- Используйте их временно при отладке: выяснить, что именно инициирует частые перерендеры компонента.
SSR и предварительная загрузка данных: onServerPrefetch
onServerPrefetch используется в серверном рендеринге (SSR). Он позволяет выполнить асинхронные операции до того, как компонент будет отрендерен на сервере. Часто здесь загружают данные, чтобы на клиенте получить уже готовую HTML-разметку.
Пример базового использования:
import { ref, onServerPrefetch } from 'vue'
export default {
setup() {
const data = ref(null)
const fetchData = async () => {
// Здесь могла бы быть реальная загрузка данных с сервера
// const response = await fetch('https://api.example.com/items')
// data.value = await response.json()
data.value = ['item1', 'item2'] // Условные данные
}
// В SSR этот хук дождется выполнения промиса перед рендерингом
onServerPrefetch(async () => {
await fetchData()
})
return {
data,
}
},
}
Особенности:
- Работает только на сервере.
- Должен возвращать промис (или быть
async), чтобы рендер ждал завершения. - На клиенте данные должны гидратироваться — обычно фреймворк вокруг Vue (Nuxt, custom-SSR) берет это на себя.
Сочетание хуков в композиционных функциях
Одна из главных причин существования хуков в Composition API — возможность выносить повторяемую логику в отдельные функции, не привязанные к конкретному компоненту.
Такие функции часто называют composables или композиционные функции. Они:
- используют
ref,computed,watch, - используют lifecycle-хуки,
- возвращают готовое API для компонента.
Смотрите, я покажу вам, как это работает на примере подписки на resize.
Пример композиционной функции с lifecycle-хуками
Создадим функцию useWindowSize, которая будет:
- следить за размером окна,
- подписываться на
resizeпри монтировании, - отписываться при размонтировании.
// useWindowSize.js
import { ref, onMounted, onBeforeUnmount } from 'vue'
export function useWindowSize() {
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const update = () => {
// Обновляем ширину и высоту окна
width.value = window.innerWidth
height.value = window.innerHeight
}
onMounted(() => {
// Подписываемся на событие resize
window.addEventListener('resize', update)
})
onBeforeUnmount(() => {
// Обязательно отписываемся при уничтожении компонента,
// который использует эту композиционную функцию
window.removeEventListener('resize', update)
})
// Возвращаем реактивные значения
return {
width,
height,
}
}
Теперь вы увидите, как это выглядит в коде компонента:
<script setup>
import { useWindowSize } from './useWindowSize'
const { width, height } = useWindowSize() // Просто вызываем функцию
</script>
<template>
<div>
<p>Ширина окна - {{ width }}</p>
<p>Высота окна - {{ height }}</p>
</div>
</template>
Важный момент:
- Хотя хуки (
onMounted,onBeforeUnmount) вызываются внутриuseWindowSize, они "привязываются" к компоненту, в котором вызвалиuseWindowSize. - Это работает, потому что вызов композиционной функции происходит внутри
setupкомпонента, а Vue "знает", для какого компонента сейчас выполняется код.
Такой подход позволяет:
- сократить дублирование кода,
- сделать логику переиспользуемой,
- тестировать ее отдельно от UI.
Особенности и ограничения lifecycle-хуков в Composition API
Хуки можно вызывать только внутри setup или композиционных функций
Главное ограничение: все lifecycle-хуки должны вызываться:
- непосредственно в
setupкомпонента, - либо в функциях, которые вызываются внутри
setup(композиционные функции).
Нельзя вызывать хуки:
- внутри обычных функций, которые вызываются позже, по событию,
- внутри
ifпосле асинхронного кода, когда теряется контекст текущего компонента.
Нарушение этого правила приведет к ошибке: Vue не будет знать, к какому компоненту "прикрепить" хук.
Неправильно:
import { onMounted } from 'vue'
function initLater() {
// Плохой пример - если функция будет вызвана не в момент выполнения setup
onMounted(() => {
console.log('Это вызовет ошибку - нет активного компонента')
})
}
export default {
setup() {
// Вызываем функцию не сразу
setTimeout(() => {
initLater()
}, 1000)
},
}
Правильно:
import { onMounted } from 'vue'
function useInit() {
// Хороший пример - функция вызывается синхронно в setup
onMounted(() => {
console.log('onMounted корректно привязан к компоненту')
})
}
export default {
setup() {
// Синхронный вызов композиционной функции внутри setup
useInit()
},
}
Можно использовать один и тот же хук несколько раз
В отличие от Options API, где каждый lifecycle-метод определялся один раз, в Composition API вы можете вызывать, например, onMounted сразу в нескольких местах:
import { onMounted } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('Первый обработчик onMounted')
})
onMounted(() => {
console.log('Второй обработчик onMounted')
})
},
}
Оба обработчика выполнятся в порядке регистрации. Это удобно, когда у вас есть несколько композиционных функций, каждая из которых использует свои lifecycle-хуки.
Порядок вызова хуков в иерархии компонентов
Важно понимать порядок срабатывания хуков при работе с родительскими и дочерними компонентами.
Общее правило:
- При монтировании: сначала хуки дочерних компонентов, затем хуки родителя.
- При размонтировании: сначала хуки родителя
beforeUnmount, потом дочерниеbeforeUnmountиunmounted, затем родительскийunmounted.
Упрощенная схема:
Монтирование:
- Дочерние: onBeforeMount → onMounted
- Родитель: onBeforeMount → onMounted
Размонтирование:
- Родитель: onBeforeUnmount
- Дети: onBeforeUnmount → onUnmounted
- Родитель: onUnmounted
Это нужно учитывать, если вы:
- рассчитываете, что дочерний компонент уже смонтирован к моменту
onMountedродителя, - или что дочерний уже очищен к моменту
onUnmountedродителя.
Типичные паттерны использования lifecycle-хуков
Подписки и события
Шаблон "подписаться в onMounted — отписаться в onBeforeUnmount" встречается очень часто.
import { onMounted, onBeforeUnmount } from 'vue'
export function useEventListener(target, event, handler) {
onMounted(() => {
// Подписываемся при монтировании компонента
target.addEventListener(event, handler)
})
onBeforeUnmount(() => {
// Отписываемся перед уничтожением компонента
target.removeEventListener(event, handler)
})
}
Затем в компоненте:
import { useEventListener } from './useEventListener'
export default {
setup() {
const handleClick = () => {
console.log('Клик по окну')
}
// Смотрите, я покажу вам, как это работает
useEventListener(window, 'click', handleClick)
},
}
Таймеры и интервалы
Еще один частый сценарий.
import { ref, onMounted, onBeforeUnmount } from 'vue'
export function useInterval(callback, delay = 1000) {
const timerId = ref(null)
onMounted(() => {
// Запускаем интервал при монтировании
timerId.value = setInterval(() => {
callback()
}, delay)
})
onBeforeUnmount(() => {
// Очищаем интервал перед уничтожением компонента
if (timerId.value !== null) {
clearInterval(timerId.value)
}
})
}
Использование:
import { ref } from 'vue'
import { useInterval } from './useInterval'
export default {
setup() {
const count = ref(0)
useInterval(() => {
// Увеличиваем счетчик каждую секунду
count.value++
}, 1000)
return {
count,
}
},
}
Асинхронные операции и отмена
При работе с сетевыми запросами полезно иметь возможность отменить их при размонтировании компонента, чтобы не обновлять состояние несуществующего компонента.
Простой пример с флагом "жив" компонента:
import { ref, onBeforeUnmount, onMounted } from 'vue'
export function useFetchData(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
let isActive = true // Флаг активности компонента
const load = async () => {
loading.value = true
error.value = null
try {
// Здесь могла бы быть реальная загрузка
// const response = await fetch(url)
// const result = await response.json()
const result = ['item1', 'item2'] // Условные данные
if (isActive) {
// Обновляем состояние только если компонент еще жив
data.value = result
}
} catch (e) {
if (isActive) {
error.value = e
}
} finally {
if (isActive) {
loading.value = false
}
}
}
onMounted(() => {
// Автоматически загружаем данные при монтировании
load()
})
onBeforeUnmount(() => {
// Помечаем, что компонент больше не активен
isActive = false
})
return {
data,
loading,
error,
reload: load, // Возможность перезагрузить данные вручную
}
}
Рекомендации по организации кода с lifecycle-хуками
Разделяйте ответственность
Не складывайте всю логику компонента в один onMounted. Лучше:
- Разбить на несколько композиционных функций (
useWindowSize,useFetchData,useKeyboardShortcuts). - Внутри каждой функции — свои хуки.
Так код получается модульным и легко поддерживаемым.
Избегайте тяжелой логики в хуках обновления
onBeforeUpdate и onUpdated вызываются часто. Если там будет тяжелый код, UI начнет "тормозить". Лучше:
- использовать
watchдля реакции на изменение конкретных данных, - использовать вычисляемые свойства (computed) для подготовки производных данных.
Явная очистка ресурсов
Всегда проверяйте:
- Все ли таймеры очищены.
- Все ли подписки сняты.
- Не остается ли асинхронных операций, которые могут изменить состояние уже уничтоженного компонента.
Чем понятнее lifecycle-границы, тем проще отлаживать приложение.
Заключение
Хуки жизненного цикла в Composition API дают вам гибкий и предсказуемый способ управлять жизнью компонента: от первой инициализации до полного удаления или повторной активации при keep-alive. Они позволяют:
- аккуратно работать с DOM в нужный момент (
onMounted,onUpdated), - освобождать ресурсы и избегать утечек (
onBeforeUnmount,onUnmounted), - интегрировать внешние библиотеки и события браузера,
- локально обрабатывать ошибки (
onErrorCaptured), - диагностировать избыточные рендеры (
onRenderTracked,onRenderTriggered), - выстраивать архитектуру на основе композиционных функций.
Ключевая идея — вызывать lifecycle-хуки только в setup и композиционных функциях, которые запускаются синхронно внутри setup. Это позволяет Vue однозначно связать их с конкретным экземпляром компонента и корректно управлять порядком вызовов.
Используя хуки жизненного цикла как строительные блоки и вынося логику в хорошо структурированные composables, вы получите код, который проще расширять, тестировать и поддерживать.
Частозадаваемые технические вопросы
1. Можно ли использовать lifecycle-хуки в обычных утилитарных функциях или модулях без Vue-компонента?
Нет. Хуки (onMounted, onUnmounted и другие) работают только в контексте активного компонента. Вы можете вызывать их:
- в
setupкомпонента, - в композиционных функциях, которые вызываются синхронно внутри
setup.
Если вам нужна утилита, не зависящая от жизненного цикла, делайте ее обычной функцией без хуков и используйте внутри composable или компонента.
2. Как протестировать код, который использует lifecycle-хуки в композиционных функциях?
Подход:
- Создайте тестовый компонент, который в
setupвызывает ваш composable. - С помощью тестового фреймворка (например, Vue Test Utils) смонтируйте компонент.
- Проверяйте эффекты после монтирования (
onMounted) и после размонтирования (onUnmounted), вызываяwrapper.unmount(). - Внешние зависимости (таймеры, события, fetch) замокайте, чтобы контролировать поведение.
3. Как выполнить логику и при первом монтировании, и при каждой активации с keep-alive?
Используйте комбинацию хуков:
import { onMounted, onActivated } from 'vue'
export function useInitWithKeepAlive(callback) {
onMounted(() => {
callback() // Выполнится один раз
})
onActivated(() => {
callback() // Выполнится при каждой активации
})
}
Так вы обеспечите единый вход для логики и при первом появлении, и при последующих возвращениях компонента из кеша.
4. Можно ли внутри одного компонента использовать и Options API хуки, и Composition API хуки одновременно?
Да, в Vue 3 это возможно, но не рекомендуется для новых компонентов. Если объединение все-таки нужно (например, при миграции):
- lifecycle-хуки Options API (
mounted) и Composition API (onMounted) будут вызываться в одном и том же жизненном цикле. - Логику лучше постепенно переносить в
setup, чтобы избежать путаницы.
5. Почему я получаю предупреждение о том, что lifecycle-хук вызван вне setup?
Причина в том, что вы вызвали onMounted (или другой хук):
- вне
setup, - или внутри функции, которую вызываете асинхронно/позже.
Решение:
- Перенесите вызов композиционной функции с хуками в
setupи вызывайте ее синхронно. - Любую асинхронную логику организуйте внутри самой композиционной функции, но так, чтобы
onMountedвызывался при выполненииsetup, а не позже.
Постройте личный план изучения Vue до уровня Middle — бесплатно!
Vue — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Vue
Лучшие курсы по теме

Vue 3 и Pinia
Антон Ларичев
TypeScript с нуля
Антон Ларичев