Хуки жизненного цикла в Vue 3 Composition API - полный разбор с примерами

28 января 2026
Автор

Олег Марков

Введение

Хуки жизненного цикла в Composition API — это фундаментальный механизм, который позволяет вам "подцепиться" к разным этапам жизни компонента Vue 3: созданию, монтированию, обновлению, уничтожению и активации или деактивации (для keep-alive). Без понимания lifecycle-hooks сложно строить предсказуемые и устойчивые к ошибкам интерфейсы.

В этой статье вы разберете, как именно работают lifecycle-хуки в Composition API, чем они отличаются от опций created, mounted и других в Options API, как правильно организовывать код и как избегать частых ошибок (например, утечек памяти из-за неочищенных подписок и обработчиков событий).

Давайте шаг за шагом посмотрим, как устроен жизненный цикл компонента и как им управлять с помощью Composition API.

Обзор жизненного цикла в Vue 3 и место Composition API

Кратко о жизненном цикле компонента

У компонента Vue есть несколько ключевых этапов:

  1. Инициализация
  2. Монтирование в DOM
  3. Обновление при изменении состояния или пропсов
  4. Демонтирование из DOM
  5. (Опционально) Активация и деактивация при использовании 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-хуки в композиционных функциях?

Подход:

  1. Создайте тестовый компонент, который в setup вызывает ваш composable.
  2. С помощью тестового фреймворка (например, Vue Test Utils) смонтируйте компонент.
  3. Проверяйте эффекты после монтирования (onMounted) и после размонтирования (onUnmounted), вызывая wrapper.unmount().
  4. Внешние зависимости (таймеры, события, 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, а не позже.
Стрелочка влевоРеактивные значения reactive values в современных фреймворкахКомпозаблы composables во Vue 3Стрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Vuex - полное руководство по управлению состоянием во Vue приложенияхРеактивные ссылки ref - полный разбор для разработчиковРеактивные объекты reactive-objects - подробное руководство с примерамиРеактивные переменные - концепция reactive и практические примерыМеханизм Provide Inject - как он работает и когда применятьPinia современный менеджер состояния для VueЛокальное состояние local state в веб разработкеГлобальное состояние в приложениях - global state
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
Функция append в Go GolangОтображение компонента mounted - практическое руководствоХуки жизненного цикла компонентов - полное руководство для разработчиковУничтожение компонента destroyed - как правильно очищать ресурсы и подпискиИнициализация данных в состоянии created - как и когда подготавливать данные в приложенииОбновление компонента beforeUpdate во VueМонтирование компонента - хук beforeMount в VueРазрушение компонента во Vue - beforeDestroy и beforeUnmountСоздание экземпляра beforeCreate - полный разбор жизненного цикла
5 библиотек для создания tree view во VueИнтеграция Vue с серверной частью и HTTPS настройкамиИнтеграция Tailwind CSS с Vue для современных интерфейсовКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Слоты компонента - концепция и практическое использованиеРегистрация компонентов component-registration в приложениях с внедрением зависимостейProps компонента в React - полный разбор с примерамиФункциональные компоненты в React - функциональный подход к построению интерфейсовСобытия компонента - events в современных интерфейсахДинамические компоненты - dynamic-componentsСоздание компонента component - практическое руководствоАсинхронные компоненты async-components - практическое руководство
Наблюдатели watchers - от паттерна до практических реализацийУправление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsШаблоны Vue templates - практическое руководство для разработчиковИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueРеактивность Vue reactivity - как это работает под капотом и как этим пользоватьсяПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueОпции компонента в Go - паттерн component-optionsРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreВычисляемые свойства computed во Vue.jsСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueОсновы Vue - vue-basics для уверенного стартаИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueТелепортация - архитектура и реализация в серверных приложенияхРабота с teleport для управления DOM во VueSuspense в React - управление асинхронными данными и ленивой загрузкойПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиПлагины Vue vue-plugins - полное практическое руководствоРуководство по nextTick для работы с DOMМиксины - mixins в современном программированииJSX в Vue с использованием плагина vue-jsxСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьКастомные элементы - Custom Elements в современном JavaScriptИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий