Vue.js: Componentes Dinâmicos e Assíncronos

Última atualização: 2026-08-26

Os componentes dinâmicos permitem que você renderize dinamicamente diferentes componentes usando uma única tag <component> — alternando entre abas, janelas modais e visualizações route com base nos dados. Os componentes assíncronos permitem que você carregue componentes sob demanda — carregando apenas o código necessário na primeira tela para melhorar o desempenho.

O componente experimental <Suspense> do Vue 3 foi projetado especificamente para lidar com o estado de carregamento de componentes assíncronos, eliminando a necessidade de escrever manualmente v-if="loading". Esta lição ajudará você a dominar três APIs principais.

1. O que você vai aprender


2. O pesadelo das 5 instruções “v-if” em uma única aba

(1) Problema: 5 guias, 10 diretivas v-if aninhadas

Alice criou um painel de administração com abas:

VUE
<!-- ❌ The "Broken" Version: 5 Tabs with 5 v-if -->
<template>
  <div class="tabs">
    <button @click="currentTab = 'home'">Home</button>
    <button @click="currentTab = 'profile'">Profile</button>
    <button @click="currentTab = 'settings'">Settings</button>
    
    <div v-if="currentTab === 'home'">
      <HomeTab />
    </div>
    <div v-else-if="currentTab === 'profile'">
      <ProfileTab />
    </div>
    <div v-else-if="currentTab === 'settings'">
      <SettingsTab />
    </div>
  </div>
</template>

O gerente de produto Charlie:

“Alice, precisamos de mais 5 abas. 10 linhas de v-if-else são impossíveis de manter. Use um componente dinâmico.”

(2) Solução com componentes dinâmicos do Vue: 1 <component> para alternar entre 5 abas

VUE
<!-- ✅ Correct Version: 1 <component> Switch -->
<template>
  <div class="tabs">
    <button 
      v-for="tab in tabs" 
      :key="tab.name"
      :class="{ active: currentTab === tab.name }"
      @click="currentTab = tab.name"
    >
      {{ tab.label }}
    </button>
    
    <!-- ✅ 1 <component> Dynamic Tag Switching -->
    <component :is="currentTabComponent" />
  </div>
</template>

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

// Synchronization Components
import HomeTab from './tabs/HomeTab.vue'
import ProfileTab from './tabs/ProfileTab.vue'

// Asynchronous Components(Laden on Demand)
const SettingsTab = defineAsyncComponent(() => import('./tabs/SettingsTab.vue'))

const tabs = [
  { name: 'home', label: 'Home' },
  { name: 'profile', label: 'Profile' },
  { name: 'settings', label: 'Settings' }
]

const currentTab = ref('home')

// Computed Properties:According to currentTab Determine which component to display
const currentTabComponent = computed(() => {
  return { home: HomeTab, profile: ProfileTab, settings: SettingsTab }[currentTab.value]
})
</script>

(3) Receita

Após utilizar componentes dinâmicos:


3. <component :is="..."> Noções básicas

(1) 5 maneiras de usar :is

VUE
<!-- Writing Style 1:Component Name String(Registered globally) -->
<component :is="'HomeTab'" />

<!-- Writing Style 2:Component Object(Most Commonly Used) -->
<component :is="HomeTab" />

<!-- Writing Style 3:Dynamically Calculated Properties -->
<component :is="currentTabComponent" />

<!-- Writing Style 4:Asynchronous Component Object -->
<component :is="asyncComponent" />

<!-- Writing Style 5:Inline Component Object -->
<component :is="{ template: '<div>Inline</div>' }" />

(2) Exemplo completo

VUE
<!-- TabContainer.vue -->
<template>
  <div>
    <button 
      v-for="tab in tabs" 
      :key="tab.name"
      @click="currentTab = tab.name"
    >
      {{ tab.label }}
    </button>
    
    <!-- Dynamic Components -->
    <component :is="currentTabComponent" />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import HomeTab from './tabs/HomeTab.vue'
import ProfileTab from './tabs/ProfileTab.vue'
import SettingsTab from './tabs/SettingsTab.vue'

const tabs = [
  { name: 'home', label: 'Home' },
  { name: 'profile', label: 'Profile' },
  { name: 'settings', label: 'Settings' }
]

const currentTab = ref('home')

const currentTabComponent = computed(() => ({
  home: HomeTab,
  profile: ProfileTab,
  settings: SettingsTab
}[currentTab.value]))
</script>
▶ Experimente

(3) 5 principais cenários de aplicação

Cena Componente dinâmico
Alternar entre abas
Roteiro/Diálogo
Formulários com várias etapas
Visualização da rota ✅ (componente interno <router-view>)
Troca de tema (layouts diferentes)

4. Componentes assíncronos defineAsyncComponent

(1) Por que os componentes assíncronos são necessários?

Os componentes padrão carregam todo o código imediatamente ao import. Componentes grandes (como editores de texto rico e bibliotecas de gráficos) não são necessários para a primeira tela, mas bloqueiam o carregamento. Os componentes assíncronos são carregados sob demanda, resultando em uma primeira tela mais rápida.

VUE
<script setup>
import { defineAsyncComponent } from 'vue'
import LoadingSpinner from './LoadingSpinner.vue'

// Asynchronous Components:Only when using ChartEditor Load its code only when needed
const ChartEditor = defineAsyncComponent({
  // 1. Load Function
  loader: () => import('./ChartEditor.vue'),
  
  // 2. Display while loading
  loadingComponent: LoadingSpinner,
  
  // 3. Display when loading fails
  errorComponent: ErrorMessage,
  
  // 4. Load Delay(Prevent Flickering)
  delay: 200
})
</script>

<template>
  <ChartEditor v-if="showEditor" />
</template>

(2) Notação simplificada

VUE
<script setup>
import { defineAsyncComponent } from 'vue'

// ✅ Simplified Version:Only loader
const ChartEditor = defineAsyncComponent(() => import('./ChartEditor.vue'))
</script>

(3) 5 cenários principais para componentes assíncronos

Cenário Carregamento assíncrono
Editor de texto rico
Biblioteca de gráficos (ECharts/D3)
Formulários extensos (com mais de 100 campos)
Janela modal
Carregamento diferido de rotas ✅ (Utilizado automaticamente pelo Vue Router)

5. <Suspense> Como lidar com componentes assíncronos

(1) O que é suspense?

O Suspense é o coordenador de carregamento assíncrono de componentes do Vue 3 — ele gerencia automaticamente o carregamento e os estados de erro dos componentes assíncronos. Diga adeus à necessidade de escrever manualmente v-if="loading".

VUE
<template>
  <!-- Suspense Automatically wait for asynchronous components to finish loading -->
  <Suspense>
    <!-- Asynchronous Components -->
    <ChartEditor :data="chartData" />
    
    <!-- Display while loading(Default Slot) -->
    <template #fallback>
      <LoadingSpinner />
    </template>
  </Suspense>
</template>

(2) 2 vagas

VUE
<template>
  <Suspense>
    <!-- Default Slot:Asynchronous Content -->
    <AsyncComponent />
    
    <!-- Fallback Slot: Display while loading -->
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

(3) 5 principais vantagens

Vantagem Descrição
Simplicidade Não é preciso codificar manualmente o estado de carregamento
Unificado Processamento unificado de vários componentes assíncronos
Aninhamento Suporta Suspense aninhado
Tratamento de erros Funciona com onErrorCaptured
Compatível com SSR Espera automática no lado do servidor

6. Componentes dinâmicos + cache com keep-alive

(1) Comportamento padrão: a troca de componentes faz com que eles sejam destruídos e reconstruídos

VUE
<template>
  <component :is="currentTabComponent" />
</template>

<!-- Switch Tab:
1. Legacy Components onBeforeUnmount
2. Legacy Components onUnmounted
3. New Component onBeforeMount
4. New Component onMounted
5. Data is reloaded every time you switch(Poor performance)
-->

(2) Cache Keep-Alive (para evitar a criação de duplicatas)

VUE
<template>
  <keep-alive>
    <component :is="currentTabComponent" />
  </keep-alive>
</template>

<!-- Switch Tab:
1. Legacy Components onDeactivated(Destroy without stopping)
2. New Component onActivated(Restore from Cache)
3. Data Retention,Scroll Position Retention
-->

(3) Configurações-chave do keep-alive 3

VUE
<template>
  <!-- include:Cache only the specified components -->
  <keep-alive include="HomeTab,ProfileTab">
    <component :is="currentTabComponent" />
  </keep-alive>
  
  <!-- exclude:Do not cache the specified component -->
  <keep-alive exclude="SettingsTab">
    <component :is="currentTabComponent" />
  </keep-alive>
  
  <!-- max: Maximum 5 Cached -->
  <keep-alive :max="5">
    <component :is="currentTabComponent" />
  </keep-alive>
</template>

7. Exemplo completo: abas dinâmicas + carregamento assíncrono

▶ Exemplo: 1. Noções básicas sobre abas dinâmicas

VUE
<template>
  <div>
    <nav>
      <button
        v-for="tab in tabs"
        :key="tab.name"
        :class="{ active: currentTab === tab.name }"
        @click="currentTab = tab.name"
      >
        {{ tab.label }}
      </button>
    </nav>
    
    <component :is="currentTabComponent" />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import HomeTab from './tabs/HomeTab.vue'
import ProfileTab from './tabs/ProfileTab.vue'

const tabs = [
  { name: 'home', label: 'Home' },
  { name: 'profile', label: 'Profile' },
  { name: 'settings', label: 'Settings' }
]
const currentTab = ref('home')

const currentTabComponent = computed(() => ({
  home: HomeTab,
  profile: ProfileTab,
  settings: SettingsTab
}[currentTab.value]))
</script>
▶ Experimente

▶ Exemplo: 2. Componentes assíncronos + Suspense

VUE
<!-- App.vue -->
<template>
  <button @click="show = !show">Toggle</button>
  
  <Suspense v-if="show">
    <!-- Asynchronous Components(Do not load on the first screen,Load only when clicked) -->
    <HeavyChart :data="data" />
    
    <template #fallback>
      <div>Loading chart...</div>
    </template>
  </Suspense>
</template>

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

const show = ref(false)

// ✅ Asynchronous Components:News import,Laden on Demand
const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue'))
const data = ref([1, 2, 3, 4, 5])
</script>
▶ Experimente

▶ Exemplo: 3. Componentes dinâmicos + cache com keep-alive

VUE
<template>
  <div>
    <button v-for="tab in tabs" @click="currentTab = tab.name">
      {{ tab.label }}
    </button>
    
    <!-- keep-alive Cache: Preserve data when switching -->
    <keep-alive :max="3">
      <component :is="currentTabComponent" />
    </keep-alive>
  </div>
</template>
▶ Experimente

▶ Exemplo: 4. Comparação de 5 maneiras de usar :is

Sintaxe Exemplo Uso
String <component :is="'HomeTab'"> Registro global (não recomendado)
Objeto componente <component :is="HomeTab"> Importação estática (mais comum)
Objeto de componente assíncrono <component :is="AsyncComponent"> Carregamento sob demanda
Propriedade calculada <component :is="currentTabComponent"> Comutação dinâmica
Objeto embutido <component :is="{ template: '...' }"> Cenário simples

▶ Exemplo: 5. Referência rápida para 5 erros comuns

Erro Sintoma Solução
:é uma string, mas não está registrada globalmente Aviso: Componente desconhecido Use um objeto de componente ou registre-o primeiro
Componentes assíncronos não têm alternativa Tela em branco durante o carregamento Envolver com Suspense
Alterar componentes para recarregar os dados Baixo desempenho Usar cache keep-alive
Erros não tratados em componentes assíncronos Falhas silenciosas Use com onErrorCaptured
Esqueci de adicionar :key ao componente dinâmico Inconsistência de estado Adicionar :key="tab.name"

▶ Exemplo: 6. 5 Principais comparações de desempenho

Modo Primeira tela Botão Aplicável
Importação direta Lenta Atualizar Widget
Importação dinâmica Rápida Lenta na primeira troca Componentes grandes
Suspense Rapidez Fluidez Prioridade à experiência do usuário
keep-alive Rápido 0 Atualização Troca frequente
Assíncrono + keep-alive ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ Melhores práticas

❓ Perguntas Frequentes

P: Qual é a diferença entre componentes dinâmicos e v-if? R: Com o v-if, mesmo que haja 5 condições e 5 ramificações, o componente ainda é carregado na íntegra. Com os componentes dinâmicos, utiliza-se uma única tag <component>, e o componente muda com base no atributo :is, tornando-o mais conciso. Os componentes assíncronos também podem ser carregados sob demanda.

P: defineAsyncComponent precisa estar no nível superior de setup? R: Sim. defineAsyncComponent(() => import('...')) deve ser chamado no nível superior de <script setup> (não dentro de uma função).

P: O Suspense é um recurso estável? R: Ele está estável na versão Vue 3.2 e posteriores. Você pode usá-lo com confiança, mas recomendamos testar exaustivamente o comportamento de fallback em ambientes de produção.

P: Os componentes dinâmicos podem passar props? R: Sim. Use v-bind diretamente: <component :is="Comp" :prop1="x" :prop2="y" />.

P: Como faço para capturar erros em componentes assíncronos? R: Use a opção errorComponent + onErrorCaptured: { loader, errorComponent: ErrorComp }. No Suspense, você pode envolver o código com onErrorCaptured.

P: Os dados dos componentes armazenados em cache pelo keep-alive são persistidos? R: Os dados são mantidos (não são destruídos) enquanto estiverem no cache. No entanto, atualizar a página ou mudar de rota limpará o cache. Use o Pinia para garantir a persistência.

P: Qual é a relação entre defineAsyncComponent e o carregamento diferido do Vue Router? R: O Vue Router usa internamente defineAsyncComponent para implementar o carregamento diferido das rotas. Essencialmente, trata-se da mesma coisa.


📖 Resumo


📝 Exercícios

  1. Questões básicas (Dificuldade: ⭐)

    Implemente um seletor de abas simples:

    • 3 abas (Página inicial/Perfil/Configurações)
    • Use <component :is> para alternar dinamicamente
    • Saída no Switch: “Componente X criado/destruído”
  2. Problemas avançados (Dificuldade: ⭐⭐)

    Implementação da tecla Tab + carregamento assíncrono:

    • 3 guias; a guia “Configurações” é carregada de forma assíncrona usando defineAsyncComponent
    • Exibir o status de carregamento (símbolo “v” escrito à mão, se estiver carregando)
    • Não atualiza a página ao voltar para a guia “Página inicial”
  3. Problema desafiador (Dificuldade: ⭐⭐⭐)

    Implementação de um sistema completo, dinâmico, assíncrono e com cache:

    1. 5 abas, das quais pelo menos 2 são carregadas de forma assíncrona
    2. Envolva com <Suspense>
    3. <keep-alive :max="3"> Cache
    4. Saída onActivated/onDeactivated ao alternar
    5. Exibir o ErrorComponent quando um componente assíncrono falhar
    6. Medir o tempo de carregamento da primeira tela (antes e depois da implementação assíncrona)
    7. TypeScript: Componentes dinâmicos fortemente tipados
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%