Средний 8 мин чтения
Жизненный цикл компонента
Хуки жизненного цикла позволяют выполнять код на определённых этапах существования компонента — от создания до уничтожения.
lifecycleonMountedonUnmountedhooksVue
Этапы жизненного цикла
Создание компонента
↓
setup() / <script setup>
↓
onBeforeMount
↓
Рендер + монтирование в DOM
↓
onMounted ← здесь доступен DOM
↓
Изменение данных → onBeforeUpdate → onUpdated
↓
Размонтирование
↓
onBeforeUnmount
↓
onUnmounted ← очистка ресурсов
Хуки Composition API
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
} from 'vue'
onBeforeMount(() => {
console.log('DOM ещё не создан')
})
onMounted(() => {
console.log('Компонент смонтирован, DOM доступен')
// Здесь: работа с DOM, запросы к API, подписки
})
onBeforeUpdate(() => {
console.log('Данные изменились, DOM ещё не обновлён')
})
onUpdated(() => {
console.log('DOM обновлён')
// Осторожно: не изменяйте данные здесь — бесконечный цикл!
})
onBeforeUnmount(() => {
console.log('Компонент скоро будет удалён')
})
onUnmounted(() => {
console.log('Компонент удалён')
// Здесь: очистка таймеров, отписки от событий
})
</script>
Практические примеры
Запрос данных при монтировании
<script setup>
import { ref, onMounted } from 'vue'
const users = ref([])
const loading = ref(true)
onMounted(async () => {
try {
const res = await fetch('/api/users')
users.value = await res.json()
} finally {
loading.value = false
}
})
</script>
Работа с DOM-элементом
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
onMounted(() => {
// Автофокус при монтировании
inputRef.value?.focus()
})
</script>
<template>
<input ref="inputRef" type="text" placeholder="Введите запрос">
</template>
Подписка и отписка от событий
<script setup>
import { onMounted, onUnmounted } from 'vue'
function handleResize() {
console.log('Размер изменился:', window.innerWidth)
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
// Обязательно убираем слушатель, иначе утечка памяти!
window.removeEventListener('resize', handleResize)
})
</script>
Таймеры и интервалы
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const time = ref(new Date())
let timer = null
onMounted(() => {
timer = setInterval(() => {
time.value = new Date()
}, 1000)
})
onUnmounted(() => {
clearInterval(timer)
})
</script>
Хуки в composables
Хуки жизненного цикла можно вызывать внутри composable — они привязываются к текущему экземпляру компонента:
// composables/useEventListener.ts
import { onMounted, onUnmounted } from 'vue'
export function useEventListener(target, event, handler) {
onMounted(() => {
target.addEventListener(event, handler)
})
onUnmounted(() => {
target.removeEventListener(event, handler)
})
}
<script setup>
import { useEventListener } from '@/composables/useEventListener'
useEventListener(window, 'resize', () => {
console.log('resize')
})
</script>
onErrorCaptured
Перехватывает ошибки из дочерних компонентов:
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
error.value = err.message
console.error('Ошибка в дочернем компоненте:', err, info)
return false // Предотвратить дальнейшее всплытие
})
</script>
<template>
<div v-if="error" class="error">{{ error }}</div>
<slot v-else />
</template>
nextTick
nextTick выполняет callback после следующего обновления DOM:
<script setup>
import { ref, nextTick } from 'vue'
const list = ref([])
async function addItem() {
list.value.push('Новый элемент')
// DOM ещё не обновлён
await nextTick()
// Теперь DOM обновлён — можно работать с ним
const lastItem = document.querySelector('.list-item:last-child')
lastItem?.scrollIntoView()
}
</script>