00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Lifecycle Hooks

Il ciclo di vita di un componente

Ogni componente Vue attraversa una serie di fasi dalla creazione alla distruzione. Gli hook del ciclo di vita permettono di eseguire codice in momenti specifici di questo processo.

Diagramma del ciclo di vita

Il flusso completo del ciclo di vita di un componente Vue 3 e il seguente.

  1. Setup - La funzione setup viene eseguita (Composition API)
  2. beforeCreate / created - Inizializzazione del componente (solo Options API)
  3. onBeforeMount - Prima che il template venga montato nel DOM
  4. onMounted - Il componente e montato nel DOM
  5. onBeforeUpdate - Prima che il DOM venga aggiornato dopo un cambio dati
  6. onUpdated - Il DOM e stato aggiornato
  7. onBeforeUnmount - Prima che il componente venga rimosso dal DOM
  8. onUnmounted - Il componente e stato rimosso dal DOM

onMounted

Eseguito dopo che il componente e stato montato nel DOM. E l’hook piu utilizzato per operazioni che richiedono l’accesso al DOM o per caricare dati iniziali.

<script setup>
import { ref, onMounted } from 'vue'

const dati = ref<any[]>([])
const isCaricamento = ref(true)
const altezzaElemento = ref(0)
const contenitore = ref<HTMLElement | null>(null)

onMounted(async () => {
  // Accesso al DOM (l'elemento esiste ora)
  if (contenitore.value) {
    altezzaElemento.value = contenitore.value.offsetHeight
  }

  // Caricamento dati iniziale
  try {
    const risposta = await fetch('/api/dati')
    dati.value = await risposta.json()
  } catch (errore) {
    console.error('Errore caricamento:', errore)
  } finally {
    isCaricamento.value = false
  }
})

// Puoi registrare piu onMounted nello stesso componente
onMounted(() => {
  console.log('Secondo onMounted - entrambi verranno eseguiti')
})
</script>

<template>
  <div ref="contenitore">
    <p v-if="isCaricamento">Caricamento...</p>
    <ul v-else>
      <li v-for="item in dati" :key="item.id">{{ item.nome }}</li>
    </ul>
  </div>
</template>

Uso con librerie esterne

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const canvasRef = ref<HTMLCanvasElement | null>(null)
let grafico: any = null

onMounted(() => {
  if (canvasRef.value) {
    // Inizializza una libreria che richiede un elemento DOM
    grafico = new Chart(canvasRef.value, {
      type: 'bar',
      data: {
        labels: ['Gen', 'Feb', 'Mar', 'Apr'],
        datasets: [{
          label: 'Vendite',
          data: [12, 19, 3, 5]
        }]
      }
    })
  }
})

onUnmounted(() => {
  // Pulisci la libreria esterna
  if (grafico) {
    grafico.destroy()
  }
})
</script>

<template>
  <canvas ref="canvasRef"></canvas>
</template>

onUpdated

Eseguito dopo che il DOM del componente e stato aggiornato a seguito di un cambio di stato reattivo.

<script setup>
import { ref, onUpdated } from 'vue'

const messaggi = ref<string[]>([])
const contenitoreLista = ref<HTMLElement | null>(null)

// Scroll automatico verso il basso quando arrivano nuovi messaggi
onUpdated(() => {
  if (contenitoreLista.value) {
    contenitoreLista.value.scrollTop = contenitoreLista.value.scrollHeight
  }
})

function aggiungiMessaggio() {
  messaggi.value.push(`Messaggio ${messaggi.value.length + 1}`)
}
</script>

<template>
  <div ref="contenitoreLista" class="lista-messaggi">
    <p v-for="(msg, i) in messaggi" :key="i">{{ msg }}</p>
  </div>
  <button @click="aggiungiMessaggio">Aggiungi messaggio</button>
</template>

Attenzione con onUpdated

Non modificare lo stato reattivo all’interno di onUpdated senza una condizione di uscita, altrimenti crei un loop infinito.

<script setup>
import { ref, onUpdated } from 'vue'

const contatore = ref(0)

onUpdated(() => {
  // SBAGLIATO: crea un loop infinito
  // contatore.value++

  // CORRETTO: con condizione di uscita
  if (contatore.value < 10) {
    console.log('Aggiornato, contatore:', contatore.value)
  }
})
</script>

onUnmounted

Eseguito dopo che il componente e stato rimosso dal DOM. Ideale per il cleanup di risorse.

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const posizioneMouse = ref({ x: 0, y: 0 })
let intervalloId: ReturnType<typeof setInterval> | null = null

// Gestore evento
function aggiornaPosizione(evento: MouseEvent) {
  posizioneMouse.value = { x: evento.clientX, y: evento.clientY }
}

onMounted(() => {
  // Aggiungi event listener
  window.addEventListener('mousemove', aggiornaPosizione)

  // Avvia un intervallo
  intervalloId = setInterval(() => {
    console.log('Tick')
  }, 1000)
})

onUnmounted(() => {
  // Rimuovi event listener
  window.removeEventListener('mousemove', aggiornaPosizione)

  // Ferma l'intervallo
  if (intervalloId) {
    clearInterval(intervalloId)
  }

  console.log('Componente smontato, risorse pulite')
})
</script>

<template>
  <p>Mouse: {{ posizioneMouse.x }}, {{ posizioneMouse.y }}</p>
</template>

Pattern comune: WebSocket cleanup

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const messaggi = ref<string[]>([])
let ws: WebSocket | null = null

onMounted(() => {
  ws = new WebSocket('wss://esempio.com/chat')

  ws.onmessage = (evento) => {
    messaggi.value.push(evento.data)
  }

  ws.onerror = (errore) => {
    console.error('Errore WebSocket:', errore)
  }
})

onUnmounted(() => {
  if (ws) {
    ws.close()
    ws = null
  }
})
</script>

onBeforeMount

Eseguito prima che il componente venga montato nel DOM. Il template e compilato ma non ancora inserito nel documento.

<script setup>
import { onBeforeMount } from 'vue'

onBeforeMount(() => {
  console.log('Il componente sta per essere montato')
  // Il DOM non e ancora disponibile qui
  // Utile per logica di inizializzazione che non richiede il DOM
})
</script>

onBeforeUpdate

Eseguito prima che il DOM venga aggiornato. Utile per accedere allo stato del DOM prima delle modifiche.

<script setup>
import { ref, onBeforeUpdate, onUpdated } from 'vue'

const lista = ref(['A', 'B', 'C'])
let altezzaPrecedente = 0

onBeforeUpdate(() => {
  // Salva lo stato del DOM prima dell'aggiornamento
  const elemento = document.getElementById('mia-lista')
  if (elemento) {
    altezzaPrecedente = elemento.scrollHeight
  }
})

onUpdated(() => {
  // Confronta con lo stato dopo l'aggiornamento
  const elemento = document.getElementById('mia-lista')
  if (elemento) {
    const nuovaAltezza = elemento.scrollHeight
    if (nuovaAltezza !== altezzaPrecedente) {
      console.log(`Altezza lista cambiata: ${altezzaPrecedente} => ${nuovaAltezza}`)
    }
  }
})
</script>

onBeforeUnmount

Eseguito prima che il componente venga rimosso dal DOM. Il componente e ancora completamente funzionale.

<script setup>
import { onBeforeUnmount } from 'vue'

onBeforeUnmount(() => {
  console.log('Il componente sta per essere smontato')
  // Utile per salvare lo stato prima della rimozione
  salvaBozza()
})

function salvaBozza() {
  // Salva i dati correnti
  localStorage.setItem('bozza', JSON.stringify({ /* dati */ }))
}
</script>

Hook per il debugging

Vue offre hook speciali utili durante lo sviluppo.

onRenderTracked e onRenderTriggered

<script setup>
import { ref, onRenderTracked, onRenderTriggered } from 'vue'

const contatore = ref(0)

// Chiamato quando una dipendenza reattiva viene tracciata durante il render
onRenderTracked((evento) => {
  // Solo in development
  console.log('Dipendenza tracciata:', evento)
  // { effect, target, type, key }
})

// Chiamato quando una dipendenza reattiva causa un re-render
onRenderTriggered((evento) => {
  console.log('Re-render causato da:', evento)
  // Utile per capire COSA causa un re-render
})
</script>

<template>
  <button @click="contatore++">{{ contatore }}</button>
</template>

onErrorCaptured

Cattura errori provenienti dai componenti discendenti.

<script setup>
import { ref, onErrorCaptured } from 'vue'

const errore = ref<string | null>(null)

onErrorCaptured((err, istanza, info) => {
  // err: l'errore catturato
  // istanza: il componente che ha generato l'errore
  // info: stringa con informazioni sulla fonte dell'errore

  errore.value = `Errore: ${err.message} (${info})`
  console.error('Errore catturato:', err, info)

  // Restituisci false per impedire la propagazione verso l'alto
  return false
})
</script>

<template>
  <div v-if="errore" class="errore">
    {{ errore }}
  </div>
  <slot v-else />
</template>

Componente ErrorBoundary

<!-- ErrorBoundary.vue -->
<script setup lang="ts">
import { ref, onErrorCaptured } from 'vue'

const props = defineProps<{
  fallback?: string
}>()

const errore = ref<Error | null>(null)

onErrorCaptured((err) => {
  errore.value = err as Error
  return false
})

function riprova() {
  errore.value = null
}
</script>

<template>
  <div v-if="errore" class="error-boundary">
    <h3>{{ fallback || 'Si e verificato un errore' }}</h3>
    <p>{{ errore.message }}</p>
    <button @click="riprova">Riprova</button>
  </div>
  <slot v-else />
</template>
<!-- Utilizzo -->
<template>
  <ErrorBoundary fallback="Errore nel caricamento del profilo">
    <ProfiloUtente :id="utenteId" />
  </ErrorBoundary>
</template>

onActivated e onDeactivated

Usati con <KeepAlive> per componenti che vengono mantenuti in cache.

<script setup>
import { ref, onActivated, onDeactivated } from 'vue'

const ultimoAccesso = ref<Date | null>(null)

// Chiamato quando il componente viene riattivato dalla cache KeepAlive
onActivated(() => {
  ultimoAccesso.value = new Date()
  console.log('Componente riattivato')
  // Aggiorna dati se necessario
})

// Chiamato quando il componente viene messo in cache da KeepAlive
onDeactivated(() => {
  console.log('Componente disattivato (in cache)')
  // Ferma operazioni non necessarie durante la cache
})
</script>

Riepilogo degli hook

Hook Quando si attiva Uso tipico
onBeforeMount Prima del mount DOM Inizializzazione senza DOM
onMounted Dopo il mount DOM Fetch dati, accesso DOM, librerie esterne
onBeforeUpdate Prima dell’aggiornamento DOM Cattura stato DOM pre-update
onUpdated Dopo l’aggiornamento DOM Operazioni DOM post-update
onBeforeUnmount Prima della rimozione Salvataggio stato
onUnmounted Dopo la rimozione Cleanup risorse
onErrorCaptured Errore in componente figlio Error boundary
onActivated Riattivazione KeepAlive Refresh dati
onDeactivated Disattivazione KeepAlive Pausa operazioni

Best practice

  • Pulisci sempre le risorse in onUnmounted: Event listener, timer, connessioni WebSocket
  • Non accedere al DOM in onBeforeMount: L’elemento non esiste ancora
  • Evita modifiche di stato in onUpdated: Rischio di loop infiniti
  • Usa onErrorCaptured per error boundaries: Gestisci errori dei componenti figli
  • Registra gli hook in modo sincrono nel setup: Non usarli in callback asincroni o setTimeout
  • Usa onMounted per le chiamate API iniziali: E il posto standard per il fetch dei dati