Vue.js: Ciclo de Vida do Componente
Última atualização: 2026-08-26
O ciclo de vida de um componente é todo o processo pelo qual um componente Vue passa, desde a criação até a destruição: instanciação → montagem no DOM → atualizações de dados → desmontagem. O Vue oferece oito ganchos de ciclo de vida que permitem executar código em momentos específicos.
Compreender o ciclo de vida é fundamental para escrever componentes “dinâmicos” — é possível carregar dados na montagem, executar efeitos colaterais na atualização e liberar recursos na desmontagem.
1. O que você vai aprender
- Um fluxograma completo do ciclo de vida do componente, desde a criação até a destruição
- 8 ganchos do ciclo de vida (onBeforeMount, onMounted, etc.)
- Sintaxe da API de composição (convenção de nomenclatura onX)
- Melhores casos de uso para cada hook
- Ordem de execução dos ciclos de vida dos componentes pai e filho
- Componentes em cache com keep-alive (ativados / desativados)
- Tratamento de erros onErrorCaptured
2. Um problema de “oscilação” em um componente de carregamento de dados
(1) Problema: O componente foi renderizado, mas os dados ainda não foram carregados
Alice criou um componente de perfil de usuário:
<!-- ❌ The "Broken" Version:Rendering Before Data Arrives -->
<template>
<div>
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref({}) // Empty object,Display during rendering "undefined"
fetch('/api/user')
.then(res => res.json())
.then(data => user.value = data)
</script>
Experiência do usuário:
- 0 ms: A página exibe “undefined undefined” (0,5 segundos)
- 500 ms: Os dados chegam; exibe “Alicealice@example.com”
- A tela pisca por 0,5 segundos, o que resulta em uma experiência do usuário muito ruim
O gerente de produto Charlie:
“Alice, os usuários veem ‘indefinido’ por meio segundo. Precisamos de um indicador de carregamento. Mostre ‘Carregando...’ até que os dados cheguem.”
(2) Ciclo de vida do Vue + Solução para o estado “Carregando”
<template>
<div>
<p v-if="loading">Loading...</p>
<div v-else>
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const user = ref(null)
const loading = ref(true)
// ✅ Load data in onMounted... (DOM Ready, loading status will be displayed first)
onMounted(async () => {
const res = await fetch('/api/user')
user.value = await res.json()
loading.value = false
})
</script>
Experiência do usuário:
- 0 ms: Exibir “Carregando...” ✅
- 500 ms: Os dados chegam; exibe “Alice alice@example.com” ✅
- Sem cintilação ✅
(3) Receita
Após adicionar o ciclo de vida:
- Problema de oscilação: 100% → 0
- Experiência do usuário: Status de carregamento claro
- Escalável: O mesmo padrão pode ser aplicado a todos os componentes de dados
3. Fluxograma do ciclo de vida completo
graph TB
A[Create a component instance] --> B[onBeforeCreate]
B --> C[setup Responsive]
C --> D[onCreated]
D --> E[Template Compilation]
E --> F[onBeforeMount]
F --> G[Mount to DOM]
G --> H[onMounted]
H --> I{Data Update?}
I -->|Yes| J[onBeforeUpdate]
J --> K[Render Again]
K --> L[onUpdated]
L --> I
I -->|No| M{Component Uninstall?}
M -->|Yes| N[onBeforeUnmount]
N --> O[Unmount]
O --> P[onUnmounted]
style H fill:#42b883,color:#fff
style L fill:#42b883,color:#fff
style P fill:#42b883,color:#fff
4. Uma explicação detalhada dos 8 ganchos do ciclo de vida
(1) Referência rápida aos 8 principais ganchos
| Hook | Condições de acionamento | Cenários comuns | Frequência |
|---|---|---|---|
onBeforeMount |
Antes da montagem dos componentes | Preparar os dados | ⭐ |
onMounted |
Após a montagem do componente | Carregamento de dados, operações no DOM | ⭐⭐⭐⭐⭐ |
onBeforeUpdate |
Antes da atualização dos dados | Otimização de desempenho | ⭐⭐ |
onUpdated |
Após a atualização dos dados | Operações após a reexibição do DOM | ⭐⭐⭐ |
onBeforeUnmount |
Antes de retirar um componente | Zerar os temporizadores | ⭐⭐⭐ |
onUnmounted |
Após desinstalar o componente | Limpeza final | ⭐⭐ |
onErrorCaptured |
Erro no componente filho | Limite de erro | ⭐⭐⭐ |
onActivated |
Keep-Alive ativado | Componente de cache restaurado | ⭐⭐ |
onDeactivated |
keep-alive desativado | componente de cache desativado | ⭐⭐ |
(2) onMounted: O mais utilizado
<script setup>
import { ref, onMounted } from 'vue'
const user = ref(null)
onMounted(async () => {
// 1. Loading initial data
const res = await fetch('/api/user')
user.value = await res.json()
// 2. DOM Operations(Realistic operation DOM)
document.title = `${user.value.name} - Dashboard`
// 3. Register a global event listener
window.addEventListener('resize', handleResize)
// 4. Set a Timer
const timer = setInterval(() => {
console.log('Tick')
}, 1000)
// ❌ Note: onUnmounted must clean up timer and listener
})
</script>
(3) onUnmounted: Liberar recursos
<script setup>
import { onMounted, onUnmounted } from 'vue'
let timer = null
onMounted(() => {
timer = setInterval(() => console.log('Tick'), 1000)
window.addEventListener('resize', handleResize)
})
// ✅ Cleanup:Avoiding Memory Leaks
onUnmounted(() => {
clearInterval(timer)
window.removeEventListener('resize', handleResize)
})
</script>
(4) onErrorCaptured: Limite de erro
<!-- ErrorBoundary.vue Parent Component -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught error:', err)
console.log('Component:', instance)
console.log('Info:', info)
error.value = err.message
return false // Prevent errors from propagating upward
})
</script>
<template>
<div>
<p v-if="error" class="error">Error: {{ error }}</p>
<slot v-else />
</div>
</template>
<!-- App.vue Usage -->
<ErrorBoundary>
<UserProfile :user-id="123" /> <!-- If an error occurs, will be caught by ErrorBoundary -->
</ErrorBoundary>
(5) 5 principais casos de uso
| Cenário | Gancho | Código |
|---|---|---|
| Carregando dados iniciais | onMounted |
await fetch(...) |
| Definir temporizador | onMounted + onUnmounted |
setInterval + clearInterval |
| Fique atento aos eventos globais | ||
| Manipulação do DOM | onMounted |
document.querySelector(...) |
| Integração de bibliotecas de terceiros | onMounted + onUnmounted |
new Chart(...) + chart.destroy() |
5. Ordem de execução dos ciclos de vida dos componentes pai e filho
(1) Ordem de montagem
sequenceDiagram
participant P as Parent
participant C as Child
P->>P: 1. Parent onBeforeMount
P->>P: 2. Parent onMounted
C->>C: 3. Child onBeforeMount
C->>C: 4. Child onMounted
Note over P,C: ❌ That's wrong: Parent mounted should come after Child
Ordem correta:
sequenceDiagram
participant P as Parent
participant C as Child
P->>P: 1. Parent onBeforeMount
C->>C: 2. Child onBeforeMount
C->>C: 3. Child onMounted
P->>P: 4. Parent onMounted
Note over P,C: ✅ Mount only after all child components, including the parent component, have finished mounting
(2) Ordem de execução completa (componentes aninhados)
1. Parent onBeforeCreate
2. Parent setup
3. Parent onCreated
4. Parent onBeforeMount
5. Child onBeforeCreate
6. Child setup
7. Child onCreated
8. Child onBeforeMount
9. Child onMounted
10. Parent onMounted
(3) Ordem de desinstalação (na ordem inversa)
1. Parent onBeforeUnmount
2. Child onBeforeUnmount
3. Child onUnmounted
4. Parent onUnmounted
6. Componente de cache Keep-Alive
(1) O que é o keep-alive?
<keep-alive> Armazena instâncias de componentes em cache para evitar a criação e destruição repetidas. É comumente usado em cenários que envolvem alternância entre abas e troca de rotas.
<!-- Parent Component -->
<template>
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">
{{ tab }}
</button>
<!-- keep-alive Package:Switch Tab Do not destroy the component -->
<keep-alive>
<component :is="currentTabComponent" />
</keep-alive>
</template>
(2) 2 novos ganchos
<!-- Child component: Cached by keep-alive -->
<script setup>
import { onActivated, onDeactivated } from 'vue'
// When the component is activated(Restore from Cache)
onActivated(() => {
console.log('Component activated')
// Reload Data、Restore scroll position, etc.
})
// When a component is disabled(Cut but keep in cache)
onDeactivated(() => {
console.log('Component deactivated')
// Save Status、Pause the timer, etc.
})
</script>
(3) 5 principais casos de uso
| Cenário | Método |
|---|---|
| Alternar entre abas | <keep-alive> Quebrar o conteúdo da aba |
| Mudança de rota | Pacote <keep-alive> <router-view> |
| Cache pop-up | <keep-alive> Janela pop-up do pacote |
| Página da lista | Listas carregadas em cache |
| Página do formulário | Armazenar em cache os formulários não enviados |
7. Exemplo completo: Componente de perfil do usuário
▶ Exemplo: 1. onMounted + onUnmounted (carregamento de dados + limpeza)
<template>
<div>
<p v-if="loading">Loading...</p>
<div v-else-if="user">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const props = defineProps({ userId: { type: Number, required: true } })
const user = ref(null)
const loading = ref(true)
let timer = null
onMounted(async () => {
// 1. Loading data
const res = await fetch(`/api/users/${props.userId}`)
user.value = await res.json()
loading.value = false
// 2. Set a Timer (Every 30s Refresh)
timer = setInterval(async () => {
const res = await fetch(`/api/users/${props.userId}`)
user.value = await res.json()
}, 30000)
// 3. Monitoring Window Size Changes
window.addEventListener('resize', () => {
console.log('Window resized')
})
})
onUnmounted(() => {
// ✅ Cleanup:Avoiding Memory Leaks
clearInterval(timer)
window.removeEventListener('resize', () => {})
})
</script>
▶ Exemplo: 2. Referência rápida para 8 ganchos
<script setup>
import {
onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted,
onErrorCaptured, onActivated, onDeactivated
} from 'vue'
// 1. Before Mounting
onBeforeMount(() => {
console.log('1. Preparing to mount')
})
// 2. After mounting(Most Commonly Used)
onMounted(() => {
console.log('2. Mounted,Loading data')
})
// 3. Before the update
onBeforeUpdate(() => {
console.log('3. Coming Soon')
})
// 4. After the update
onUpdated(() => {
console.log('4. Updated')
})
// 5. Before Uninstalling
onBeforeUnmount(() => {
console.log('5. Preparing to uninstall,Free Up Resources')
})
// 6. After uninstallation
onUnmounted(() => {
console.log('6. Uninstalled')
})
// 7. Error Handling
onErrorCaptured((err) => {
console.error('7. Child Component Error:', err)
return false
})
// 8. keep-alive Activate
onActivated(() => {
console.log('8. keep-alive Activate')
})
// 9. keep-alive Disable
onDeactivated(() => {
console.log('9. keep-alive Disable')
})
</script>
▶ Exemplo: 3. Ordem do ciclo de vida dos componentes pai e filho
<!-- Parent.vue -->
<template>
<Child :data="parentData" />
</template>
<script setup>
import { onMounted } from 'vue'
onMounted(() => console.log('4. Parent mounted'))
</script>
<!-- Child.vue -->
<template>
<p>{{ data }}</p>
</template>
<script setup>
import { onMounted } from 'vue'
onMounted(() => console.log('3. Child mounted'))
</script>
<!-- Order of Console Output:
1. Child created
2. Child mounted
3. Parent mounted
(Because the parent component must wait until the child component has finished mounting before it can mount itself) -->
▶ Exemplo: 4. Limite de erro onErrorCaptured
<!-- ErrorBoundary.vue -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught error:', err)
console.log('Component:', instance?.$options.name)
console.log('Info:', info) // 'render' / 'watch' / 'lifecycle hook'
error.value = err.message
return false // Prevent upward transmission
})
</script>
<template>
<div v-if="error" class="error-banner">
⚠️ Error: {{ error }}
</div>
<slot v-else />
</template>
▶ Exemplo: 5. Componente de cache keep-alive
<!-- TabContainer.vue -->
<template>
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab"
:class="{ active: currentTab === tab }"
@click="currentTab = tab"
>
{{ tab }}
</button>
<!-- keep-alive Cache: Preserve component state when switching away -->
<keep-alive>
<component :is="currentTabComponent" />
</keep-alive>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import Home from './tabs/Home.vue'
import Profile from './tabs/Profile.vue'
import Settings from './tabs/Settings.vue'
const tabs = ['Home', 'Profile', 'Settings']
const currentTab = ref('Home')
const currentTabComponent = computed(() => {
return { Home, Profile, Settings }[currentTab.value]
})
</script>
▶ Exemplo: 6. Referência rápida para 5 erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
Use document no nível superior de setup |
Erro do servidor | Use onMounted |
| Temporizador não foi liberado | Vazamento de memória | Limpar em onUnmounted |
| Modificar props no onMounted | Aviso do Vue | Use emit em vez disso |
| Componentes assíncronos não tratam erros | Falha silenciosa | onErrorCaptured |
| Ordem incorreta de montagem pai-filho | Confusão na depuração | Compreendendo o princípio “pai é igual a filho” |
❓ Perguntas Frequentes
P: Qual é a diferença entre
onMountedeonCreated? R: QuandoonCreatedé chamado, o componente já foi criado, mas o DOM ainda não foi renderizado (portanto, não é possível manipular o DOM). QuandoonMountedé chamado, o DOM já foi montado (portanto, é possível manipular o DOM). O carregamento de dados geralmente é tratado emonMounted(para evitar problemas de SSR).
P: Os hooks de ciclo de vida podem ser assíncronos? R: Sim.
onMounted(async () => { await fetch(...) })é uma sintaxe válida. O Vue não espera que a operação assíncrona seja concluída antes de continuar.
P: Qual é a diferença entre
onActivatedeonMountedemkeep-alive? R:onMountedé acionado apenas uma vez (na montagem inicial).onActivatedé acionado toda vez que a visualização é restaurada do cache. A troca de abas acionaonActivatedeonDeactivated(nãoonMountedeonUnmounted).
P: Que tipos de erros o
onErrorCapturedpode capturar? R: Ele pode capturar três tipos: (1) erros de renderização de componentes filhos; (2) erros nos ganchos do ciclo de vida de componentes filhos; (3) erros nos callbacks de observação de componentes filhos. No entanto, ele não pode capturar seus próprios erros nem erros assíncronos.
P: Um componente pai pode monitorar os eventos do ciclo de vida de um componente filho? R: Sim. Você pode usar @hook para monitorar:
@hook:mounted="handleChildMounted". No entanto, recomenda-se usar props/emit para a comunicação, em vez de monitorar diretamente os eventos do ciclo de vida.
P: Quais são as diferenças entre os hooks de ciclo de vida do Vue 2 e do Vue 3? R: O Vue 3 renomeou o hook para
onMounted(que eramountedno Vue 2) e adicionouonErrorCapturedeonActivated/onDeactivated. Todos os ganchos do Vue 2 estão disponíveis no Vue 3 (a compatibilidade com versões anteriores é mantida).
P: O que acontece com o ciclo de vida durante a SSR (renderização no lado do servidor)? R: onBeforeMount, onMounted, onBeforeUpdate e onUpdated não são acionados no servidor (já que não há DOM). Apenas onBeforeCreate, onCreated, onBeforeUnmount, onUnmounted e onErrorCaptured são acionados.
📖 Resumo
- 8 ganchos do ciclo de vida: onBeforeMount / onMounted / onBeforeUpdate / onUpdated / onBeforeUnmount / onUnmounted / onErrorCaptured / onActivated
- onMounted (mais comumente usado): manipulação do DOM, carregamento de dados, registro de eventos
- onUnmounted: Limpeza obrigatória: temporizadores, ouvintes de eventos, bibliotecas de terceiros
- Ordem pai-filho: pai antes do beforeMount → filho antes do beforeMount → filho montado → pai montado
- keep-alive + onActivated/onDeactivated para componentes em cache
- onErrorCaptured: Implementar um limite de erro
- Alguns hooks não são acionados durante a renderização no lado do servidor
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Implemente um componente UserCard simples:
- onMounted: Carregar os dados do usuário (simular um atraso de 1 segundo)
- Mostrar “Carregando” → Mostrar informações do usuário
- em registros de desmontagem: “Componente destruído”
-
Problemas avançados (Dificuldade: ⭐⭐)
Implementar um componente Timer:
- onMounted: Inicia um cronômetro (+1 por segundo)
- Exibir o segundo atual
- onUnmounted: Limpar o temporizador
- Adicione um botão para pausar/retomar (use
refpara salvar o estado)
-
Problema desafiador (Dificuldade: ⭐⭐⭐)
Implementar um sistema completo de abas:
- TabContainer.vue: 3 abas (Página inicial/Perfil/Configurações)
- 3 componentes de conteúdo em abas
- Utilize o componente de cache
<keep-alive> - Alterar a saída da guia “X ativado, Y desativado”
- Carregue os dados em uma das abas usando
onMounted - A conexão não é encerrada ao sair da página (devido ao keep-alive), portanto, os dados ainda estão lá ao voltar à página
- Use
onErrorCapturedpara detectar erros nos componentes filhos