O que é o Vue Vapor Mode?
Vue Vapor Mode explicado: SFCs viram operações DOM diretas, veja o que muda no Vue 3.6 RC e os riscos com eventos e slots.
O Vue Vapor Mode é um modo de compilação para single-file components do Vue que transforma templates em operações diretas no DOM, de modo que a renderização e as atualizações acontecem sem criar ou fazer diff de um virtual DOM, o que reduz o tamanho base do bundle, o custo de atualização e o uso de memória.
O Vapor já vem sendo apresentado em conferências há algum tempo, mas ainda não existe uma página sobre ele na documentação do Vue. Os detalhes estão nas release notes do Vue core.
Este artigo aborda o que o Vapor Mode muda na forma como seus componentes executam e em que ponto ele está no ciclo de releases. Também trata de dois comportamentos do Vapor que falham silenciosamente e nos quais é fácil esbarrar.
Principais Conclusões
- O Vapor Mode compila SFCs do Vue em operações diretas no DOM em vez de criação e diff de VNodes, e é daí que vêm o bundle menor e as atualizações mais rápidas.
- O Vapor Mode está feature-complete nos release candidates do Vue 3.6, mas não é estável: a linha 3.6 ainda está em pré-lançamento, com a v3.6.0-rc.9 (18 de setembro de 2026) marcada como Pre-release no GitHub, enquanto o rótulo Latest permanece na linha 3.5, na v3.5.43 (17 de setembro de 2026).
- O Vapor é opt-in por componente, via
<script setup vapor>,<script vapor>ou<template vapor>, e funciona apenas em SFCs que contêm somente template e em SFCs que usamscript setup; a Options API não é suportada. createVaporApp()monta uma aplicação puramente Vapor que nunca carrega o runtime do virtual DOM; instalar ovaporInteropPluginpara hospedar componentes VDOM traz esse runtime de volta e anula o ganho de tamanho.- Componentes Vapor não têm VNodes nem proxy público de instância, então
getCurrentInstance()retorna null,app.config.globalPropertiesnão se aplica e template refs de componentes deixam de expor$el,$props,$attrs,$slotsou$refs.
Como Funciona o Vue Vapor Mode?
A release note da v3.6.0-rc.1 apresenta o Vapor como uma segunda forma de compilar single-file components, voltada a um bundle inicial menor e a um melhor desempenho. Nada disso acontece por padrão: você o ativa manualmente, um componente por vez, e a parte da API do Vue que ele cobre se comporta majoritariamente como você já espera. O código-fonte que você escreve não muda. O que muda é a saída: em vez de uma render function que produz VNodes para o runtime comparar com a árvore anterior, o compilador emite código que cria os nós uma única vez e conecta cada dependência reativa à atualização específica do DOM que ela controla.
Remover o virtual DOM elimina três custos de uma vez. O próprio runtime de diffing nunca precisa ser enviado em uma aplicação puramente Vapor, então o bundle base é menor. As atualizações pulam a alocação de VNodes e a comparação de árvores, de modo que uma alteração em um ref afeta um nó de texto ou um atributo. E, como nenhuma árvore-sombra é mantida entre renderizações, o consumo de memória por componente diminui.
O componente em si parece código comum da Composition API:
<script setup vapor>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>
<template>
<button @click="count++">{{ count }} / {{ doubled }}</button>
</template>
Apenas o atributo vapor difere do mesmo componente compilado em modo VDOM.
Status de Release: Feature-Complete, Não Estável
O Vapor Mode não foi lançado em uma versão estável do Vue. Na lista de releases do vuejs/core, a linha 3.6 ainda está em release candidates: a v3.6.0-rc.9, publicada em 18 de setembro de 2026, carrega o rótulo Pre-release, enquanto o rótulo Latest pertence à v3.5.43, publicada em 17 de setembro de 2026. O npm confirma: a dist-tag latest do pacote vue aponta para a 3.5.43, e os release candidates ficam sob a tag separada rc. Não existe uma tag estável 3.6.0.
O que é verdade é mais restrito e fácil de confundir com um lançamento. A release note da v3.6.0-rc.1 afirma que o Vapor Mode está feature-complete no Vue 3.6 RC, e foi por isso que a linha 3.6 chegou a entrar em release candidates. Feature-complete descreve escopo, não estabilidade. A própria política de releases do Vue trata todo pré-lançamento da mesma forma: instável, existente para que times testem como um build se encaixa em sua stack em vez de executá-lo em produção, e livre para quebrar compatibilidade de um build para outro. Se você instalar um, fixe a versão exata.
Como Habilitar o Vapor Mode?
O Vapor é habilitado por componente, não por projeto. Dois tipos de componente se qualificam: um single-file component que contém apenas um template e um escrito com script setup. Componentes na Options API não podem ser compilados para Vapor de forma alguma. Há três maneiras de marcar um componente que se qualifica: a forma completa <script setup vapor>, seu atalho <script vapor> e um marcador vapor na tag do template, que compila o arquivo inteiro como Vapor.
<!-- Form 1: the explicit form -->
<script setup vapor>
// ...
</script>
<!-- Form 2: shorthand for <script setup vapor> -->
<script vapor>
// ...
</script>
<!-- Form 3: marks the whole SFC as Vapor -->
<template vapor>
<!-- ... -->
</template>
A consequência prática é uma etapa de auditoria. Qualquer componente ainda escrito com data, methods ou mounted precisa ser convertido para script setup antes que a flag vapor faça qualquer diferença para ele.
É Possível Misturar Componentes Vapor e Virtual DOM?
Componentes Vapor e virtual DOM podem ser misturados, e a forma como você monta a aplicação define o que acaba no bundle. Se todos os componentes forem Vapor, monte com createVaporApp(): esse caminho deixa o runtime do virtual DOM fora do build, e é daí que vem a queda acentuada no tamanho base. Uma aplicação montada com createApp() precisa instalar o vaporInteropPlugin antes de conseguir renderizar um filho Vapor. Uma aplicação Vapor pode instalar o mesmo plugin para hospedar filhos virtual DOM, mas fazer isso traz o runtime de volta e abre mão da maior parte da economia de tamanho, como explica a release note da 3.6.0-rc.1.
// Pure Vapor: the VDOM runtime is never loaded
import { createVaporApp } from 'vue'
import App from './App.vue'
createVaporApp(App).mount('#app')
// Existing VDOM app hosting Vapor components
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
createApp(App).use(vaporInteropPlugin).mount('#app')
Um componente escrito como render function ou em JSX também é um componente virtual DOM, então ele precisa de interoperabilidade dentro de uma aplicação Vapor. A interoperabilidade não é passe livre. Aninhar um modo dentro do outro lida com props, eventos e slots comuns, mas ainda não com todos os casos extremos, e uma biblioteca de componentes construída sobre o virtual DOM ainda pode se comportar mal sob o Vapor. A recomendação do time do Vue é dar a cada região da aplicação um único modo de renderização e manter o aninhamento misto ao mínimo.
Do Que Componentes Vapor Abrem Mão?
Um componente Vapor não tem VNodes nem proxy público de instância. Cada item da lista de recursos não suportados na release note da rc.1 decorre dessa única fronteira, e cada um tem uma consequência concreta:
| Não suportado no Vapor | O que de fato quebra |
|---|---|
| Options API | Componentes que usam data, methods ou opções de ciclo de vida não podem ser compilados para Vapor de forma alguma. |
app.config.globalProperties | Globais injetadas por plugins ficam ausentes dentro de componentes Vapor; injete explicitamente o que você precisa. |
getCurrentInstance() | Retorna null, então código de terceiros que busca a instância interna falha dentro de componentes Vapor. |
Eventos de ciclo de vida de elemento @vue:xxx | Hooks por elemento não existem mais; use um template ref junto com onMounted. |
v-memo | A válvula de escape de memoização manual não está disponível e precisa ser removida. |
$el, $props, $attrs, $slots, $refs em template refs de componentes | Padrões em que o pai alcança o filho quebram; mova o contrato para props e emits. |
A nota também é cuidadosa quanto ao grau dessa equivalência. O Vapor se propõe a se comportar como o virtual DOM, mas os dois renderizadores são construídos de formas tão diferentes que pequenas divergências em casos extremos são esperadas, e uma divergência tão pequena só conta como breaking change se o comportamento antigo estava documentado.
Duas Armadilhas Que Vale Conhecer Antes de Começar
Eventos Delegados e stopPropagation()
No design da rc.1, eventos que podem ser delegados são tratados no nível do document. O elemento mantém seu handler, mas nada é vinculado ao elemento em si. Um único listener no document faz o trabalho: ele segue o caminho percorrido pelo evento e dispara qualquer handler que encontre ao longo dele. Se um ancestral chamar stopPropagation() durante a subida, o evento nunca chega ao document, e portanto esse handler nunca é executado. Três formas pulam a delegação e anexam o listener diretamente ao elemento: @[event]="onClick", v-bind="{ onClick }" e v-on="{ click: onClick }".
<script setup vapor>
const onClick = () => save()
</script>
<template>
<!-- the ancestor stops propagation, so a delegated handler never fires -->
<div @click.stop>
<button @click="onClick">Save</button>
</div>
<!-- binds directly to the element instead -->
<div @click.stop>
<button v-on="{ click: onClick }">Save</button>
</div>
</template>
Esse modo de falha é silencioso. Nada lança erro, nada é logado, e o monitoramento de erros reporta uma sessão saudável enquanto o usuário clica em um controle que não faz nada. O session replay é a técnica que revela isso, porque você pode observar um clique seguido de nenhuma mutação no DOM, que é a assinatura de um handler que nunca foi executado. O mesmo vale nas fronteiras entre Vapor e VDOM, onde lacunas de interoperabilidade tendem a aparecer como esquisitices de renderização em vez de exceções.
A maior parte das mudanças ao longo da linha RC está em hydration e slots, com apenas algumas correções na forma como os event listeners são mesclados, então fixe um RC exato e leia o CHANGELOG do branch minor referente à versão que você instalar, em vez de presumir que a descrição da rc.1 ainda se aplica.
slots.default() Renderiza, Não Informa
No Vapor, chamar slots.default() não é uma consulta gratuita ao slot. A chamada executa o código de renderização do slot, o que pode construir Blocks e nós do DOM, configurar efeitos reativos e assumir o controle do DOM que o servidor já enviou, caso a página esteja em hydration. O hábito comum do VDOM de chamar um slot para decidir se renderiza um fallback tem, portanto, consequências no Vapor.
<script setup vapor>
import { useSlots } from 'vue'
const slots = useSlots()
// Wrong: this call renders the slot rather than inspecting it
const showFallback = !slots.default?.()
</script>
Expresse a decisão no template e deixe o template ser dono da renderização do slot:
<template>
<slot>Fallback</slot>
</template>
Quem Deve Experimentar o Vapor Mode Agora
A release note cita dois usos neste estágio: colocar o Vapor em parte de uma aplicação que você já tem, como uma página em que a velocidade de renderização importa, e escrever uma nova aplicação pequena em Vapor desde o início. Vale enunciar o corolário com clareza. Um projeto com política de dependências restrita a versões estáveis, uma tela construída sobre uma biblioteca de componentes VDOM e uma base de código ainda na Options API são todos maus candidatos iniciais, porque o primeiro sequer pode instalar um pré-lançamento e os outros dois se encontram exatamente onde a interoperabilidade e a lista de recursos não suportados pesam.
Planejando em Torno do Vapor Mode
Trate o Vapor Mode como uma mudança de compilador que você pode avaliar hoje em uma tela, não como uma migração a ser agendada. Fixe um release candidate exato, converta uma única página com muitas listas ou muitas animações para <script setup vapor> e verifique a lista de releases do vuejs/core antes de planejar qualquer coisa em torno de uma 3.6 estável.
Perguntas Frequentes
O Vapor Mode funciona com Nuxt?
Sim, de forma experimental, e apenas no Vue 3.6 ou mais recente. O Nuxt mantém a raiz da aplicação no virtual DOM e permite que você marque componentes ou páginas individuais como Vapor, então a adoção é gradual. Uma aplicação Nuxt inteiramente em Vapor ainda não é possível, um template ref apontando para um componente Vapor não lhe dará o elemento e, se o Vue instalado for mais antigo, o Nuxt emite um aviso e desativa a opção novamente.
Preciso do Vapor Mode para obter as melhorias de desempenho da reatividade do Vue 3.6?
Não. A linha 3.6 reconstrói o núcleo de reatividade do Vue sobre o alien-signals, e os ganhos resultantes em velocidade e uso de memória se aplicam a toda aplicação nessa linha de release, sem nada para ativar. O Vapor Mode é uma mudança de compilação separada e por componente, então uma aplicação virtual DOM padrão rodando na 3.6 já obtém as melhorias de reatividade sem habilitar o Vapor em lugar algum.
O Vapor Mode suporta renderização no servidor e hydration?
A hydration em SSR está incluída no conjunto de recursos do Vapor nos release candidates do Vue 3.6, depois que os primeiros builds alpha foram lançados sem ela. A correção da hydration tem sido uma das áreas com mais mudanças ao longo da linha de pré-lançamento, então fixe um release candidate exato e teste a saída do servidor, a hydration e a recuperação de mismatch em uma página real, em vez de presumir paridade com a renderização em virtual DOM.
Uma biblioteca de componentes de terceiros funcionará dentro de um componente Vapor?
Teste antes de se comprometer. Componentes distribuídos como render functions ou JSX continuam sendo componentes virtual DOM e precisam de interoperabilidade, e código de biblioteca que chama getCurrentInstance ou lê globalProperties não encontra nada dentro de um componente Vapor. Qualquer coisa construída sobre provide e inject, em vez de sobre a instância do componente, normalmente continua funcionando, e é por isso que o Nuxt afirma que a maioria de seus próprios composables e componentes embutidos não precisa de alterações sob o Vapor.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k