← Курс/Lifecycle Hooks: жизненный цикл компонента#223 из 257+30 XP

Lifecycle Hooks: жизненный цикл компонента

Что такое lifecycle

Каждый компонент Vue проходит несколько этапов от создания до удаления. На каждом этапе можно выполнить код с помощью **lifecycle hooks** — функций-обработчиков.

Создание      Монтирование    Обновление      Размонтирование
─────────     ────────────    ──────────      ───────────────
setup()   →   onBeforeMount → onBeforeUpdate → onBeforeUnmount
              onMounted     → onUpdated      → onUnmounted

Основные хуки Vue 3

Монтирование

import { onMounted, onBeforeMount } from 'vue'

export default {
  setup() {
    onBeforeMount(() => {
      // DOM ещё не создан
      // Используй для подготовки данных перед рендером
      console.log('Компонент готовится к монтированию')
    })

    onMounted(() => {
      // DOM уже создан и доступен
      // Используй для: fetch данных, подписок, работы с DOM
      console.log('Компонент смонтирован')
      fetchData()
      window.addEventListener('resize', handleResize)
    })
  }
}

Обновление

onBeforeUpdate(() => {
  // Реактивные данные изменились, но DOM ещё не обновлён
  // Можно прочитать старое состояние DOM
})

onUpdated(() => {
  // DOM обновлён в соответствии с новыми данными
  // Осторожно: не изменяй реактивные данные здесь — вызовет бесконечный цикл!
})

Размонтирование

onBeforeUnmount(() => {
  // Компонент ещё в DOM
  // Начинай очистку
})

onUnmounted(() => {
  // Компонент удалён из DOM
  // Обязательно очищай подписки, таймеры, слушатели событий!
  window.removeEventListener('resize', handleResize)
  clearInterval(timer)
  subscription.unsubscribe()
})

Options API vs Composition API

| Options API | Composition API | Когда вызывается |

|----------------------|-----------------------|-------------------------------|

| beforeCreate | setup() (начало) | Перед инициализацией |

| created | setup() (конец) | После инициализации |

| beforeMount | onBeforeMount | Перед вставкой в DOM |

| mounted | onMounted | После вставки в DOM |

| beforeUpdate | onBeforeUpdate | Перед обновлением DOM |

| updated | onUpdated | После обновления DOM |

| beforeUnmount | onBeforeUnmount | Перед удалением из DOM |

| unmounted | onUnmounted | После удаления из DOM |

Типичные паттерны использования

setup() {
  const data = ref(null)
  const loading = ref(false)

  // Загрузка данных при монтировании
  onMounted(async () => {
    loading.value = true
    data.value = await fetchUsers()
    loading.value = false
  })

  // Очистка при размонтировании
  let intervalId
  onMounted(() => {
    intervalId = setInterval(refreshData, 5000)
  })
  onUnmounted(() => {
    clearInterval(intervalId)
  })

  return { data, loading }
}

onErrorCaptured

onErrorCaptured((error, instance, info) => {
  console.error('Ошибка в дочернем компоненте:', error)
  return false // предотвращает дальнейшее распространение ошибки
})

Примеры

Реализация lifecycle системы через callbacks — аналог Vue lifecycle в чистом JS

// Минималистичная lifecycle система
function createLifecycleComponent(name) {
  // Хранилище callbacks для каждого хука
  const hooks = {
    beforeMount: [],
    mounted: [],
    beforeUpdate: [],
    updated: [],
    beforeUnmount: [],
    unmounted: [],
  }

  // Регистрация хука (аналог onMounted, onUpdated и т.д.)
  function registerHook(hookName, callback) {
    if (!hooks[hookName]) {
      throw new Error(`Неизвестный хук: ${hookName}`)
    }
    hooks[hookName].push(callback)
  }

  // Вызов всех callbacks для хука
  function callHook(hookName, ...args) {
    console.log(`[Lifecycle] ${name}: ${hookName}`)
    hooks[hookName].forEach(cb => cb(...args))
  }

  let data = null
  let isMounted = false

  return {
    // Регистрация хуков (аналог onMounted и т.д.)
    onBeforeMount: (cb) => registerHook('beforeMount', cb),
    onMounted: (cb) => registerHook('mounted', cb),
    onBeforeUpdate: (cb) => registerHook('beforeUpdate', cb),
    onUpdated: (cb) => registerHook('updated', cb),
    onBeforeUnmount: (cb) => registerHook('beforeUnmount', cb),
    onUnmounted: (cb) => registerHook('unmounted', cb),

    // Методы управления жизненным циклом
    mount(initialData) {
      if (isMounted) throw new Error('Компонент уже смонтирован')
      callHook('beforeMount')
      data = initialData
      isMounted = true
      callHook('mounted')
    },

    update(newData) {
      if (!isMounted) throw new Error('Компонент не смонтирован')
      callHook('beforeUpdate', { old: data, new: newData })
      const oldData = data
      data = newData
      callHook('updated', { old: oldData, new: newData })
    },

    unmount() {
      if (!isMounted) throw new Error('Компонент не смонтирован')
      callHook('beforeUnmount')
      isMounted = false
      data = null
      callHook('unmounted')
    },

    getData: () => data,
  }
}

// Использование
const comp = createLifecycleComponent('UserList')

// Регистрируем хуки — как в setup() с onMounted и т.д.
let timer = null

comp.onBeforeMount(() => console.log('  Готовимся к монтированию...'))
comp.onMounted(() => {
  console.log('  Компонент смонтирован, запускаем таймер')
  timer = 'interval_42' // Имитируем setInterval
})
comp.onBeforeUpdate(({ old: o, new: n }) => console.log(`  Обновление: "${o}" -> "${n}"`))
comp.onUpdated(() => console.log('  DOM обновлён'))
comp.onBeforeUnmount(() => {
  console.log(`  Очищаем таймер ${timer}`)
  timer = null
})
comp.onUnmounted(() => console.log('  Компонент удалён'))

// Запускаем жизненный цикл
comp.mount('initial data')
comp.update('updated data')
comp.update('final data')
comp.unmount()