Mit der neusten Version 3 von Vue.js wurden einige Herausforderungen angegangen, die bei intensiverer Nutzung früher oder später auftauchten. Darüberhinaus wurde mit der Composition API eine Möglichkeit geschaffen, Funktionalitäten noch besser zu kapseln und damit die Les-, Wart- und Erweiterbarkeit zu verbessern.
Hierfür wurden Änderungen vorgenommen, die man sich vor dem Umstieg genauer anschauen sollte. Wir werden uns hier unter anderem auf einige neue Features beschränken, die wir uns bei der Nutzung von Vue.js 2 gewünscht hätten.
Hier ein Überblick über die folgenden Themen:

  • Instanziierung
  • Provide & Inject
  • Teleport
  • Mutli-Root Nodes
  • Composition API

Instanziierung

Eine sehr wichtige Änderung betrifft die Instanziierung von Vue-Applikationen und das Anlegen neuer Komponenten. Es existiert nun eine genauere Beziehung von Applikation zu Komponenten und Direktiven. Diese sind immer an eine bestimmte App gebunden, wie hier:

let app = Vue.createApp({...})

app.component('komponente-1', {...})
app.directive('direktive-1', {...})

// Alt:
Vue.component('komponente-1', {...})
Vue.component('direktive-1', {...})
let app = new Vue({...})

Die App wird dann über die Funktion mount() an ein HTML-Element gebunden.

app.mount('#el')

// Alt:
new Vue({
    el: '#el',
}) // oder app.$mount('$el')

Dadurch müssen nun alle Komponenten, Direktiven etc. an jede Applikation extra gebunden werden und sind nicht mehr global verfügbar.

Provide & Inject

Vor allem in sehr verschachtelten Komponent-Strukturen müssen oft Variablen durchgereicht werden. Bei folgender Struktur ist es allerdings schon sehr aufwändig, wenn wir plötzlich in der neuen Komponente SearchInFile eine Variable aus dem Eltern-Element benötigen.

App                  <- Variable: searchOptions
 - Folder
  - DragAndDrop
   - Folder
   - File
    - SearchInFile   <- Variable hier benötigt 
  - SearchInFolder

Abhilfe schafft hier das neue Provide-&-Inject-Schema. Die Variable wird über provide im Elternelement für alle Kinder zur Verfügung gestellt. Mittels inject können diese dann in den Komponenten verwendet werden.

let app = Vue.createApp({
  ...,
  provide() {
    return {
      searchOptions: this.searchOptions
    }
  }
})

app.component('search-in-file', {
  ...,
  inject: ['searchOptions']
})

Achtung: Hier sollte auf die Reaktivität der Variablen geachtet werden, damit Änderungen der Variable im Elternelement auch in den Kindern behandelt werden. searchOptions im obigen Beispiel ist nicht reaktiv. Hier kann die Composition API Abhilfe schaffen.

Teleport

Manchmal möchten wir ein Teil des HTMLs in unserer App woanders hin auslagern, aber dafür nicht die Struktur in der Applikation ändern. Über das neue teleport-Tag können wir HTML in andere Bereiche des DOMs verschieben. Angenommen wir möchten ein Modal immer an den Body hängen, damit das Styling korrekt ist:

<body>
  <div id="app">
    <header></header>
    <content></content>
    
    <teleport to="body">
      <modal></modal>      <- Das hier
    </teleport>
  </div>
  <!-- Wird hier hin geschoben -->
</body>

Der Teil innerhalb des teleport-Tags wird an unseren Body angehangen, d.h. nach #app.

Multi-Root Nodes

In Vue 2 durften HTML-Templates von Komponenten nur aus einem Wurzel-Tag bestehen. Das führte dazu, dass die HTML-Struktur immer in ein extra div-Tag eingebunden war. In Vue 3 wurde dies geändert:

<template>
  <header>...</header>
  <article>...</article>
  <footer>...</footer>
</template>

#Alt:
<template>
  <div>
    <header>...</header>
    <article>...</article>
    <footer>...</footer>
  </div>
</template>

Composition API

Das Besondere in der neuen Version ist die Composition API. Hiermit bietet uns Vue die Möglichkeit verschiedene Funktionalitäten innerhalb von Komponenten gesondert zu kapseln. Außerdem können wir die Daten in den Komponenten genauer verwalten.
Der Einstieg in die neue API ist die setup-Funktion. Diese wird vor dem eigentlichen Mounting ausgeführt. Von hier aus müssen nun alle Variablen, Computed Properties, Watcher und Lifecycle-Hooks über neue Funktionen hinzugefügt werden. Hier ein einfaches Beispiel:

app.component('blog-post', {
  setup(props) {
    let fullPost = Vue.reactive(props.post)
    let title = fullPost.title
    let subtitle = fullPost.subtitle

    let fullTitle = Vue.ref(title)
    if ( subtitle ) fullTitle.value += ': ' + subtitle

    watchEffect(() => {
      title = newTitle.split(':')[0]
      subtitle = newTitle.split(':').count > 1 ? newTitle.split(':')[1] : ''
    })

    return {
      fullTitle: fullTitle,
    }
  }
})

Die Funktionen Vue.reactive und Vue.ref erstellen hierbei neue reaktive Variablen. Die Funktion watchEffect ist ähnlich zu den Watcher aus Vue 2. Das Besondere hier ist, dass wir den Code einfach in eine Funktion auslagern können, welche ein Object zurück gibt.

app.component('blog-post', { 
  setup(props) { 
    let fullPost = Vue.reactive(props.post) 

    function fullTitleFunction() {
      let title = fullPost.title
      // Rest
      return {
        fullTitle: fullTitle
      }
    }
    
    return {
      ...fullTitleFunction()
    }
  }
})

Ein vollständiges Beispiel mit einer Code-Aufteilung nach Funktionalität ist ganz unten zu finden.

Fazit

Einige der Neuerungen vereinfachen die Verwendung von Vue. Gerade mit der Composition API ist es möglich den Code besser zu strukturieren, auch wenn es eine gewisse Umstellung bedeutet. Ein großer Nachteil der neuen Version ist die bisher noch fehlende Kompatibilität zur Vorgängerversion. Dadurch kann Vue 2 Code nur bedingt mit der neuen Version ausgeführt werden, was die Verwendung vor allem auf neue Projekte beschränkt.

Weitere Informationen rund um Vue 3 können hier abgerufen werden.

See the Pen
Vue 3 Blog-Overview
by Sascha Schweitzer (@Visyu-Sascha)
on CodePen.