12k
All articles

Was ist der Vue Vapor Mode?

Vue Vapor Mode erklärt: SFCs werden zu direkten DOM-Operationen kompiliert, plus Änderungen in Vue 3.6 RC und Fallen bei Events und Slots.

OpenReplay Team
OpenReplay Team
Was ist der Vue Vapor Mode?

Vue Vapor Mode ist ein Kompilierungsmodus für Vue Single-File Components, der Templates in direkte DOM-Operationen überführt. Rendering und Updates erfolgen dadurch ohne Erzeugung oder Diffing eines Virtual DOM, was die Basisgröße des Bundles, die Update-Kosten und den Speicherverbrauch senkt.

Vapor wird bereits seit einiger Zeit auf Konferenzen gezeigt, dennoch gibt es in der Vue-Dokumentation noch keine Seite dazu. Die Details finden sich in den Release Notes des Vue-Cores.

Dieser Artikel behandelt, was der Vapor Mode an der Ausführung Ihrer Komponenten verändert und wo er im Release-Zyklus steht. Außerdem geht es um zwei Verhaltensweisen in Vapor, die stillschweigend fehlschlagen und in die man leicht hineinläuft.

Die wichtigsten Erkenntnisse

  • Der Vapor Mode kompiliert Vue-SFCs in direkte DOM-Operationen statt in VNode-Erzeugung und -Diffing – daher rühren das kleinere Bundle und die schnelleren Updates.
  • Der Vapor Mode ist in den Release Candidates von Vue 3.6 feature-complete, aber nicht stabil: Die 3.6-Linie befindet sich weiterhin im Pre-Release-Status, wobei v3.6.0-rc.9 (18. September 2026) auf GitHub als Pre-release gekennzeichnet ist, während das Label „Latest“ mit v3.5.43 (17. September 2026) bei der 3.5-Linie bleibt.
  • Vapor wird pro Komponente über <script setup vapor>, <script vapor> oder <template vapor> aktiviert und funktioniert nur bei reinen Template-SFCs sowie bei SFCs mit script setup; die Options API wird nicht unterstützt.
  • createVaporApp() mountet eine reine Vapor-App, die die Virtual-DOM-Runtime nie lädt; wird vaporInteropPlugin installiert, um VDOM-Komponenten einzubinden, kommt diese Runtime wieder hinzu und der Größenvorteil entfällt größtenteils.
  • Vapor-Komponenten besitzen keine VNodes und keinen öffentlichen Instanz-Proxy: getCurrentInstance() gibt null zurück, app.config.globalProperties greift nicht, und Component Template Refs stellen $el, $props, $attrs, $slots oder $refs nicht mehr bereit.

Wie funktioniert der Vue Vapor Mode?

Die Release Note zu v3.6.0-rc.1 stellt Vapor als zweiten Weg vor, Single-File Components zu kompilieren – mit dem Ziel eines kleineren Ausgangs-Bundles und besserer Performance. Nichts davon geschieht standardmäßig: Sie aktivieren es selbst, Komponente für Komponente, und der abgedeckte Teil der Vue-API verhält sich weitgehend so, wie Sie es ohnehin erwarten. Der Quellcode, den Sie schreiben, ändert sich nicht. Was sich ändert, ist die Ausgabe: Statt einer Render-Funktion, die VNodes erzeugt, damit die Runtime sie gegen den vorherigen Baum diffen kann, gibt der Compiler Code aus, der die Knoten einmal erstellt und jede reaktive Abhängigkeit mit genau dem DOM-Update verdrahtet, das sie steuert.

Der Wegfall des Virtual DOM eliminiert gleich drei Kostenfaktoren. Die Diffing-Runtime selbst muss in einer reinen Vapor-App nie ausgeliefert werden, wodurch das Basis-Bundle kleiner ausfällt. Updates überspringen VNode-Allokation und Baumvergleich, sodass eine Änderung an einer Ref genau einen Textknoten oder ein Attribut berührt. Und da zwischen Renderdurchläufen kein Schattenbaum vorgehalten wird, sinkt der Speicherbedarf pro Komponente.

Die Komponente selbst sieht aus wie gewöhnlicher Composition-API-Code:

<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>

Nur das Attribut vapor unterscheidet sie von derselben Komponente, die im VDOM-Modus kompiliert wird.

Release-Status: Feature-complete, nicht stabil

Der Vapor Mode ist in keinem stabilen Vue-Release enthalten. In der Release-Liste von vuejs/core steht die 3.6-Linie weiterhin bei Release Candidates: v3.6.0-rc.9, veröffentlicht am 18. September 2026, trägt das Label „Pre-release“, während das Label „Latest“ zu v3.5.43 gehört, veröffentlicht am 17. September 2026. npm bestätigt das: Der Dist-Tag latest des vue-Pakets verweist auf 3.5.43, und die Release Candidates liegen hinter dem separaten Tag rc. Ein stabiles 3.6.0-Tag existiert nicht.

Was zutrifft, ist enger gefasst und wird leicht mit einem Release verwechselt. Die Release Note zu v3.6.0-rc.1 hält fest, dass der Vapor Mode im RC von Vue 3.6 feature-complete ist – deshalb ist die 3.6-Linie überhaupt in die Release-Candidate-Phase übergegangen. „Feature-complete“ beschreibt den Funktionsumfang, nicht die Stabilität. Die Release Policy von Vue behandelt jedes Pre-Release gleich: instabil, gedacht dafür, dass Teams testen können, wie ein Build zu ihrem Stack passt, statt ihn produktiv einzusetzen – und frei darin, die Kompatibilität von einem Build zum nächsten zu brechen. Wenn Sie eines installieren, pinnen Sie die exakte Version.

Wie aktiviert man den Vapor Mode?

Vapor wird pro Komponente aktiviert, nicht pro Projekt. Zwei Arten von Komponenten kommen infrage: eine Single-File Component, die ausschließlich ein Template enthält, und eine mit script setup geschriebene. Komponenten auf Basis der Options API lassen sich überhaupt nicht zu Vapor kompilieren. Es gibt drei Möglichkeiten, eine geeignete Komponente zu kennzeichnen: das ausführliche <script setup vapor>, dessen Kurzform <script vapor> und ein vapor-Marker am Template-Tag, der die gesamte Datei als Vapor kompiliert.

<!-- 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>

Die praktische Konsequenz ist ein Audit-Schritt. Jede Komponente, die noch mit data, methods oder mounted geschrieben ist, muss auf script setup umgestellt werden, bevor das vapor-Flag für sie irgendeine Wirkung entfaltet.

Lassen sich Vapor- und Virtual-DOM-Komponenten mischen?

Vapor- und Virtual-DOM-Komponenten lassen sich mischen, und die Art, wie Sie die App mounten, entscheidet darüber, was im Bundle landet. Ist jede Komponente eine Vapor-Komponente, mounten Sie mit createVaporApp(): Dieser Weg lässt die Virtual-DOM-Runtime aus dem Build heraus – daher der deutliche Rückgang der Basisgröße. Eine mit createApp() gemountete App muss vaporInteropPlugin installieren, bevor sie ein Vapor-Kind rendern kann. Eine Vapor-App kann dasselbe Plugin installieren, um Virtual-DOM-Kinder einzubinden, holt sich damit aber die Runtime zurück und gibt den Größenvorteil größtenteils auf, wie die Release Note zu 3.6.0-rc.1 erläutert.

// 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')

Eine als Render-Funktion oder in JSX geschriebene Komponente ist ebenfalls eine Virtual-DOM-Komponente und benötigt daher Interop innerhalb einer Vapor-App. Interop ist kein Freifahrtschein. Das Verschachteln eines Modus im anderen beherrscht gewöhnliche Props, Events und Slots, aber noch nicht jeden Sonderfall, und eine auf dem Virtual DOM aufbauende Komponentenbibliothek kann sich unter Vapor weiterhin fehlerhaft verhalten. Die Empfehlung des Vue-Teams lautet, jedem Bereich einer App einen einzigen Rendering-Modus zuzuweisen und gemischte Verschachtelung auf ein Minimum zu beschränken.

Worauf verzichten Vapor-Komponenten?

Eine Vapor-Komponente hat keine VNodes und keinen öffentlichen Instanz-Proxy. Jeder Eintrag auf der Liste nicht unterstützter Features in der Release Note zu rc.1 ergibt sich aus dieser einen Grenze, und jeder Punkt hat eine konkrete Folge:

Nicht unterstützt in VaporWas tatsächlich bricht
Options APIKomponenten, die data, methods oder Lifecycle-Optionen verwenden, lassen sich überhaupt nicht zu Vapor kompilieren.
app.config.globalPropertiesPer Plugin injizierte Globals fehlen innerhalb von Vapor-Komponenten; injizieren Sie Benötigtes explizit.
getCurrentInstance()Gibt null zurück, sodass Drittanbietercode, der auf die interne Instanz zugreift, innerhalb von Vapor-Komponenten fehlschlägt.
@vue:xxx-Lifecycle-Events für ElementeHooks pro Element entfallen; verwenden Sie stattdessen eine Template Ref plus onMounted.
v-memoDer manuelle Memoization-Ausweg steht nicht zur Verfügung und muss entfernt werden.
$el, $props, $attrs, $slots, $refs bei Component Template RefsMuster, bei denen das Elternteil in das Kind hineingreift, brechen; verlagern Sie den Vertrag auf Props und Emits.

Die Note ist auch vorsichtig darin, wie eng diese Übereinstimmung ist. Vapor zielt darauf ab, sich wie der Virtual DOM zu verhalten, doch die beiden Renderer sind so unterschiedlich aufgebaut, dass kleine Abweichungen in Randfällen zu erwarten sind – und eine derart kleine Abweichung gilt nur dann als Breaking Change, wenn das alte Verhalten dokumentiert war.

Zwei Fallstricke, die man vorher kennen sollte

Delegierte Events und stopPropagation()

Im Design von rc.1 werden Events, die delegiert werden können, auf document-Ebene behandelt. Das Element behält seinen Handler, aber nichts wird am Element selbst gebunden. Ein einzelner Listener auf dem Document erledigt die Arbeit: Er folgt dem Weg, den das Event nimmt, und löst jeden Handler aus, dem er dabei begegnet. Ruft ein Vorfahre auf dem Weg nach oben stopPropagation() auf, erreicht das Event document nie, und der Handler wird nie ausgeführt. Drei Schreibweisen umgehen die Delegation und hängen den Listener direkt an das Element: @[event]="onClick", v-bind="{ onClick }" und 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>

Dieser Fehlerfall verläuft stillschweigend. Nichts wirft eine Exception, nichts wird geloggt, und das Error-Monitoring meldet eine gesunde Session, während der Nutzer auf ein Bedienelement klickt, das nichts tut. Session Replay ist die Technik, die so etwas sichtbar macht, denn Sie können nach einem Klick suchen, auf den keine DOM-Mutation folgt – die Signatur eines Handlers, der nie ausgeführt wurde. Dasselbe gilt an den Grenzen zwischen Vapor und VDOM, wo sich Interop-Lücken eher als Rendering-Auffälligkeiten denn als Exceptions zeigen.

Der Großteil der Änderungen über die RC-Linie hinweg betrifft Hydration und Slots, mit nur wenigen Fixes an der Art, wie Event-Listener zusammengeführt werden. Pinnen Sie also einen exakten RC und lesen Sie das CHANGELOG des Minor-Branches für die Version, die Sie installieren, statt anzunehmen, die Beschreibung aus rc.1 gelte weiterhin.

slots.default() rendert – es liefert keine Auskunft

In Vapor ist der Aufruf von slots.default() kein folgenloser Blick auf den Slot. Der Aufruf führt den Rendering-Code des Slots aus, was Blocks und DOM-Knoten erzeugen, reaktive Effects aufsetzen und – falls die Seite gerade hydratisiert – die Kontrolle über bereits vom Server geliefertes DOM übernehmen kann. Die im VDOM verbreitete Gewohnheit, einen Slot aufzurufen, um zu entscheiden, ob ein Fallback gerendert werden soll, hat in Vapor daher Konsequenzen.

<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>

Formulieren Sie die Entscheidung im Template und überlassen Sie dem Template das Rendern des Slots:

<template>
  <slot>Fallback</slot>
</template>

Wer sollte den Vapor Mode jetzt ausprobieren?

Die Release Note nennt zwei Einsatzszenarien für dieses Stadium: Vapor in einen Teil einer bestehenden App einzubringen, etwa auf einer Seite, bei der die Rendering-Geschwindigkeit zählt, und eine kleine neue App von Anfang an in Vapor zu schreiben. Die Umkehrung sollte man klar benennen: Ein Projekt mit einer Abhängigkeitsrichtlinie, die nur stabile Versionen zulässt, ein auf einer VDOM-Komponentenbibliothek aufgebauter Screen und eine Codebasis, die noch auf der Options API läuft, sind allesamt schlechte erste Kandidaten – das erste kann ein Pre-Release überhaupt nicht installieren, und die beiden anderen liegen genau dort, wo Interop und die Liste nicht unterstützter Features zuschlagen.

Planung rund um den Vapor Mode

Betrachten Sie den Vapor Mode als Compiler-Änderung, die Sie heute an einem einzelnen Screen evaluieren können – nicht als Migration, die Sie einplanen. Pinnen Sie einen exakten Release Candidate, stellen Sie eine einzelne listen- oder animationslastige Seite auf <script setup vapor> um, und prüfen Sie die Release-Liste von vuejs/core, bevor Sie etwas rund um ein stabiles 3.6 planen.

FAQs

Funktioniert der Vapor Mode mit Nuxt?

Ja, experimentell und nur ab Vue 3.6. Nuxt belässt die Wurzel der App beim Virtual DOM und erlaubt es, einzelne Komponenten oder Seiten als Vapor zu markieren, sodass Sie es schrittweise einführen. Eine reine Vapor-Nuxt-App ist noch nicht möglich, eine Template Ref auf eine Vapor-Komponente liefert Ihnen das Element nicht, und ist das installierte Vue älter, warnt Nuxt und schaltet die Option wieder ab.

Brauche ich den Vapor Mode, um von den Reaktivitäts-Performanceverbesserungen in Vue 3.6 zu profitieren?

Nein. Die 3.6-Linie baut den Reaktivitätskern von Vue auf Basis von alien-signals neu auf, und die daraus resultierenden Gewinne bei Geschwindigkeit und Speicherverbrauch gelten für jede App dieser Release-Linie, ohne dass etwas aktiviert werden muss. Der Vapor Mode ist eine separate, komponentenweise Kompilierungsänderung; eine normale Virtual-DOM-App auf 3.6 erhält die Reaktivitätsverbesserungen also bereits, ohne Vapor irgendwo einzuschalten.

Unterstützt der Vapor Mode Server-Side Rendering und Hydration?

SSR-Hydration ist im Funktionsumfang von Vapor in den Release Candidates von Vue 3.6 enthalten, nachdem frühe Alpha-Builds noch ohne sie ausgeliefert wurden. Die Korrektheit der Hydration war einer der Bereiche mit den meisten Änderungen über die Pre-Release-Linie hinweg. Pinnen Sie deshalb einen exakten Release Candidate und testen Sie Server-Ausgabe, Hydration und Mismatch-Recovery an einer echten Seite, statt Parität mit dem Virtual-DOM-Rendering vorauszusetzen.

Funktioniert eine Komponentenbibliothek von Drittanbietern innerhalb einer Vapor-Komponente?

Testen Sie es, bevor Sie sich festlegen. Als Render-Funktionen oder JSX ausgelieferte Komponenten bleiben Virtual-DOM-Komponenten und benötigen Interop, und Bibliothekscode, der getCurrentInstance aufruft oder globalProperties ausliest, findet innerhalb einer Vapor-Komponente nichts vor. Was auf provide und inject statt auf der Komponenteninstanz aufbaut, funktioniert in der Regel weiterhin – deshalb gibt Nuxt an, dass die meisten seiner eigenen Composables und eingebauten Komponenten unter Vapor keine Änderungen benötigen.

DevTools for the frontend

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

We use cookies to improve your experience. By using our site, you accept cookies.