Vue 3 Teleport e Suspense: Padrões Avançados e Perguntas de Entrevista 2026
Domine Teleport e Suspense no Vue 3.5 com padrões de produção, tratamento de erros e as perguntas de entrevista técnica mais frequentes para desenvolvedores Vue.

Vue 3 Teleport e Suspense resolvem dois desafios distintos de renderização: posicionar componentes fora de sua hierarquia DOM e coordenar dependências assíncronas. Ambos são componentes nativos que não requerem importações e aparecem frequentemente em entrevistas para desenvolvedores Vue seniores.
O Vue 3.5 adicionou a prop defer ao Teleport, permitindo apontar para elementos renderizados posteriormente na árvore de componentes. Isso resolve o problema comum de timing no SSR onde o alvo do portal não existe no momento da montagem.
Fundamentos do Teleport e a Prop defer
O Teleport move o conteúdo renderizado para uma localização DOM diferente enquanto preserva a árvore de componentes Vue. O componente permanece como filho lógico de seu pai: props, eventos e provide/inject funcionam normalmente. O Vue DevTools mostra o componente teleportado aninhado sob seu pai, não sob o elemento alvo.
<!-- ModalTrigger.vue -->
<script setup>
import { ref } from 'vue'
const isOpen = ref(false)
</script>
<template>
<button @click="isOpen = true">Open Modal</button>
<Teleport to="#modal-root">
<div v-if="isOpen" class="modal-overlay" @click.self="isOpen = false">
<div class="modal-content">
<slot />
<button @click="isOpen = false">Close</button>
</div>
</div>
</Teleport>
</template>A prop to aceita uma string de seletor CSS ou uma referência de elemento DOM. Quando o alvo não existe no momento da montagem, o Teleport emite um aviso e não renderiza nada. Isso cria problemas com SSR e layouts dinâmicos.
A Prop defer (Vue 3.5+)
A prop defer instrui o Teleport a aguardar até que outras partes da aplicação sejam montadas antes de resolver o alvo. Isso permite teleportar para elementos renderizados posteriormente no mesmo componente ou em componentes irmãos.
<!-- App.vue -->
<template>
<Teleport defer to="#dynamic-target">
<NotificationBanner />
</Teleport>
<!-- Target rendered after the Teleport -->
<div id="dynamic-target"></div>
</template>Sem defer, este código falha porque #dynamic-target não existe quando o Teleport monta. Com defer, o Vue resolve o alvo após todos os componentes no mesmo tick terem sido montados. O alvo ainda deve ser renderizado no mesmo ciclo de montagem ou atualização.
Múltiplos Teleports e Renderização Condicional
Múltiplos componentes Teleport podem apontar para o mesmo elemento. O conteúdo é adicionado na ordem de declaração:
<template>
<Teleport to="#notifications">
<Toast message="First" />
</Teleport>
<Teleport to="#notifications">
<Toast message="Second" />
</Teleport>
</template>
<!-- Result in #notifications:
<div>First</div>
<div>Second</div>
-->A prop disabled controla se a teleportação acontece. Quando desabilitada, o conteúdo renderiza no lugar original. Este padrão lida com layouts responsivos onde modais devem sobrepor no desktop mas renderizar inline no mobile:
<script setup>
import { useMediaQuery } from '@vueuse/core'
const isMobile = useMediaQuery('(max-width: 768px)')
</script>
<template>
<Teleport to="body" :disabled="isMobile">
<MobileDrawer />
</Teleport>
</template>Suspense para Orquestração de Componentes Assíncronos
O Suspense coordena dependências assíncronas através de uma árvore de componentes. Quando qualquer componente descendente possui uma dependência assíncrona não resolvida, o Suspense exibe o conteúdo do slot fallback. Uma vez que todas as dependências sejam resolvidas, o Suspense alterna para o slot padrão.
O Suspense permanece experimental no Vue 3.5. A API pode mudar antes de alcançar status estável. O uso em produção requer aceitar este risco.
<!-- Dashboard.vue -->
<template>
<Suspense>
<template #default>
<DashboardContent />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
</template>O Suspense rastreia dois tipos de dependências assíncronas: componentes com async setup() e componentes assíncronos criados com defineAsyncComponent.
Setup Assíncrono com Top-Level Await
O Vue 3 script setup suporta top-level await. Qualquer componente usando top-level await se torna uma dependência assíncrona que o Suspense pode rastrear:
<!-- UserProfile.vue -->
<script setup>
const response = await fetch('/api/user/profile')
const user = await response.json()
</script>
<template>
<div class="profile">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</template>Este componente não pode renderizar até que o fetch complete. Um boundary Suspense pai exibe o fallback até a resolução. Sem um boundary Suspense, o componente simplesmente não renderiza.
Suspense Aninhado com a Prop suspensible
Aplicações complexas frequentemente possuem múltiplos boundaries assíncronos. O Vue 3.3 introduziu a prop suspensible para controlar como componentes Suspense aninhados interagem com seus pais.
<!-- PageLayout.vue -->
<template>
<Suspense>
<template #default>
<header>
<AsyncNavigation />
</header>
<main>
<!-- Inner Suspense with its own fallback -->
<Suspense suspensible>
<template #default>
<AsyncContent />
</template>
<template #fallback>
<ContentSkeleton />
</template>
</Suspense>
</main>
</template>
<template #fallback>
<PageSkeleton />
</template>
</Suspense>
</template>Quando suspensible está definido, o Suspense interno se registra com o boundary Suspense pai. O pai aguarda tanto AsyncNavigation quanto AsyncContent resolverem antes de ocultar seu fallback. O Suspense interno ainda serve como boundary local: se AsyncContent demorar mais, ContentSkeleton é exibido enquanto AsyncNavigation permanece visível.
Sem suspensible, o Suspense interno opera independentemente. O pai mostra seu fallback apenas enquanto AsyncNavigation carrega, ignorando AsyncContent completamente.
Pronto para mandar bem nas entrevistas de Vue.js / Nuxt.js?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Combinando Teleport, Suspense e Transições
Aplicações reais combinam esses padrões. Um modal que carrega conteúdo assincronamente se beneficia dos três:
<!-- AsyncModal.vue -->
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const isOpen = ref(false)
const AsyncModalContent = defineAsyncComponent({
loader: () => import('./ModalContent.vue'),
loadingComponent: () => import('./ModalSkeleton.vue'),
delay: 200,
timeout: 10000
})
</script>
<template>
<button @click="isOpen = true">Open</button>
<Teleport to="body">
<Transition name="modal">
<div v-if="isOpen" class="modal-wrapper">
<Suspense>
<template #default>
<AsyncModalContent @close="isOpen = false" />
</template>
<template #fallback>
<ModalSkeleton />
</template>
</Suspense>
</div>
</Transition>
</Teleport>
</template>
<style>
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
</style>A ordem de aninhamento importa: Teleport envolve Transition, que envolve o elemento condicional, que contém Suspense. Isso garante que a transição anime todo o modal incluindo estados de carregamento.
Tratamento de Erros com Suspense
O Suspense não trata erros. Dependências assíncronas que falham rejeitam suas promises e propagam erros para cima na árvore de componentes. O hook de ciclo de vida onErrorCaptured captura esses erros no boundary:
<!-- ErrorBoundary.vue -->
<script setup>
import { ref, onErrorCaptured } from 'vue'
const error = ref(null)
onErrorCaptured((err) => {
error.value = err
return false // Prevent propagation
})
</script>
<template>
<div v-if="error" class="error-state">
<p>Failed to load: {{ error.message }}</p>
<button @click="error = null">Retry</button>
</div>
<Suspense v-else>
<template #default>
<slot />
</template>
<template #fallback>
<slot name="loading" />
</template>
</Suspense>
</template>Este padrão envolve qualquer árvore de componentes assíncronos com estados tanto de carregamento quanto de erro. O botão de tentar novamente limpa o erro, fazendo o Vue re-renderizar e re-tentar a operação assíncrona.
Perguntas de Entrevista sobre Teleport e Suspense
Posições Vue seniores testam esses padrões cada vez mais. Perguntas comuns e o que distingue respostas fortes:
P: Quando o Teleport deve ser usado ao invés de posicionamento CSS?
Resposta fraca: "Quando z-index não funciona."
Resposta forte: O Teleport resolve problemas de hierarquia DOM que CSS não consegue resolver. Um modal dentro de um container com overflow: hidden ou transform cria um novo contexto de empilhamento, quebrando position: fixed. O Teleport move o modal para body, escapando dessas restrições enquanto preserva a árvore lógica de componentes Vue.
P: Como o Suspense difere de estados de carregamento em cada componente?
Resposta fraca: "Suspense é mais limpo."
Resposta forte: O Suspense coordena múltiplas dependências assíncronas. Dez componentes com estados de carregamento individuais mostram dez spinners em momentos diferentes. O Suspense mostra um spinner até que todos os dez resolvam, então os revela juntos. Isso cria uma performance percebida mais suave e código de componente mais simples já que componentes assíncronos não precisam gerenciar seus próprios estados de carregamento.
P: O que acontece quando um alvo Teleport não existe?
O conteúdo não renderiza e o Vue registra um aviso. A prop defer do Vue 3.5 resolve isso aguardando que outros componentes montem primeiro. Sem defer, é necessário garantir que os alvos existam no index.html ou em um componente pai que monte antes dos filhos.
P: O Suspense funciona com Options API?
Sim. Qualquer componente com async setup() aciona o Suspense, independentemente de usar Composition API ou Options API em outro lugar. A função setup é o boundary assíncrono, não o estilo do componente.
Teleport e Suspense no Nuxt 3
O Nuxt 3 estende esses padrões com considerações de SSR. A documentação do Nuxt sobre SSR cobre o timing de hidratação.
Para o Teleport, o Nuxt requer que o alvo exista antes da hidratação. É necessário adicionar alvos de portal ao app.vue ou usar a prop defer:
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<!-- Portal targets for SSR -->
<div id="modal-root"></div>
<div id="toast-root"></div>
</template>Para o Suspense, useFetch e useAsyncData do Nuxt se integram automaticamente. Componentes de página usando esses composables funcionam com o Suspense nativo do Nuxt sem configuração manual. Consulte o módulo de composables Vue do SharpSkill para preparação relacionada a entrevistas.
Padrões de Produção para Vue 3.5
- Alvos Teleport no index.html: Criar alvos de portal estáveis que existam antes de qualquer código Vue executar. Isso evita problemas de timing completamente.
- Boundaries Suspense granulares: Um Suspense por unidade de carregamento independente. Um dashboard com widgets separados se beneficia de boundaries Suspense separados para que widgets rápidos apareçam imediatamente.
- Boundaries de erro acima do Suspense: Sempre envolver Suspense com tratamento de erros. Falhas de rede e erros de API não devem crashar toda a árvore.
- Timing de transições: Ao combinar Suspense com Transition, configurar
mode="out-in"para prevenir sobreposição durante mudanças de estado.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em Vue.js / Nuxt.js?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 28 de agosto de 2026
Tags
Compartilhar
Artigos relacionados

Vue 3 Script Setup e defineModel em 2026: Sintaxe Moderna e Perguntas de Entrevista
Guia completo sobre a sintaxe script setup do Vue 3 e defineModel. Exploração de padrões modernos, TypeScript e preparação para entrevistas técnicas de Vue.js.

Vue 3 Reactivity Transform em 2026: $ref, $computed e Perguntas de Entrevista
Guia completo sobre Reactivity Transform do Vue 3, incluindo $ref, $computed, macros de reatividade e perguntas frequentes em entrevistas técnicas.

Vue 3 com TypeScript em 2026: props, emits e composables com tipos seguros
Padrões de Vue 3 e TypeScript para 2026: defineProps, defineEmits, composables, defineModel e provide/inject tipados, com exemplos práticos e perguntas de entrevista.