Vue Grundlagen

Themen

  • Ãœberblick über Vue
  • deklararives Rendering / Arbeiten mit State
  • Vue Template-Sprache
  • Vue Developer Tools
  • Initialisieren eines Vue-Projekts mittels Vue CLI
  • Komponenten
  • Verwenden vordefinierter Komponenten
  • das Composition API
  • Side Effects

Vue Ãœberblick

Was ist Vue?

  • Eines der 3 großen JavaScript UI Libraries / Frameworks (neben Angular, React)

Grundlagen moderner JavaScript UI Libraries

  • State-basiert / deklarativ
  • Komponenten-Struktur

State-basiert / Deklarativ

  • Datenmodell, das den gesamten Anwendungszustand (State) abbildet
  • User-Interaktionen ändern das Modell, das View wird automatisch aktualisiert

Komponenten-Struktur

  • "eigene" HTML-Tags
  • Datenfluss via Properties und Events
  • Ãœblicherweise unidirektionaler Datenfluss (vom Eltern- zum Kindelement)

Beispiel: Komponenten und State in einer Todo-App

image/svg+xml TodoItem TodoList AddTodo TodoApp todos newTitle TodoItem ...

Beispiel: Props und Events in einer Todo-App

image/svg+xml TodoItem TodoList AddTodo TodoApp todos newTitle TodoItem ... todos onToggle onDelete onAdd todo onToggle onDelete todo onToggle onDelete

Vue im Vergleich zu anderen Frameworks

  • einfacher als andere
  • kann ohne einen Build-Schritt verwendet werden

Geschichte von Vue

  • erste Veröffentlichung 2014
  • 2020: Vue 3 (neues Composition API)

Online Editor

Online Editor

Empfehlung:

https://codesandbox.io

  • Templates für Vue und Vue 3
  • basiert auf VS Code
  • Prettier-basierte Formatierung mittels Shift + Alt + F
  • Hosting unter z.B. https://abcde.csb.app

Options API Grundlagen

Options API und composition API

  • options API: "traditionelle" Möglichkeit, Vue-Komponenten zu schreiben
  • composition API: neue Möglichkeit, eingeführt 2020 mit Vue 3 (inspiriert durch React Hooks)

Komponentendefinition

allgemeine Komponentendefinition in einer .vue-Datei:

<template>
...
</template>

<script>
...
</script>

<style scoped>
...
</style>

Beispiel: Slideshow-Komponente

<div>
  <button @click="imgId = 0">start</button>
  <button @click="prevImg()">previous</button>
  <img :src="imgUrl" alt="slideshow" />
  <button @click="imgId++">next</button>
</div>

Beispiel: Slideshow-Komponente

export default {
  name: 'MySlideshow',
  data() {
    return { imgId: 0 };
  },
  computed: {
    imgUrl() {
      return `https://picsum.photos/200?image=${this.imgId}`;
    },
  },
  methods: {
    prevImg() {
      if (this.imgId > 0) {
        this.imgId--;
      }
    },
  },
};

Options API: Grundlagen

angegebene Properties und Methoden in einer Komponentendefition:

  • name: wird in den Entwicklerwerkzeugen angezeigt
  • data: Komponenten-State
  • computed: vom State abgeleitete Werte
  • methods: Event Handler, ...
  • ...

Data, methods, computed, ...

Einträge in data, methods, computed, ... sind im Skript als this.entryname und im Template als entryname verfügbar

State

State wird mittels der data-Methode initialisiert

State-Einträge sind reaktiv: Vue kann reagieren, wenn sie sich ändern (und das Rendering der Komponente entsprechend aktualiseren)

Methoden

Methoden sind Funktionen, die mit einer Komponente assoziiert sind

  • können aus dem Template aufgerufen werden
  • können auf den State zugreifen

Computed

  • Funktionen in computed können abgeleitete Daten berechnen
  • im Allgemeinen sollte eine Komponente den minimalen State speichern (z.B. die Bild-ID, nicht die ganze Bild-URL, vermeiden redundanter Daten)
  • Funktionen in computed werden automatisch aufgerufen, wenn sich eine Abhängigkeit ändert

Computed

Wie weiß Vue, wann Werte in computed aktualisert werden müssen?

Während dem ersten Berechnen eines computed-Wertes überprüft Vue, auf welche State-Einträge zugegriffen wird - diese zählen später als Trigger für die Aktualisierung

Templatesprache: Grundlagen

Templatesprache: Grundlagen

  • Binden von Inhalten
  • Binden von Properties
  • Binden von Events
  • if / else
  • Wiederholen von Elementen
  • Two-Way-Binding für Inputs
  • Whitespace

Binden von Inhalten

<div>A year has {{365 * 24}} hours.</div>

Binden von Properties

Kurzform:

<a :href="'https://en.wikipedia.org/wiki/' + topic">
  some article
</a>

Langform:

<a v-bind:href="'https://en.wikipedia.org/wiki/' + topic">
  some article
</a>

Class-Property

besondere Syntax für die class-Property:

<div :class="{todo: true, completed: isCompleted}">...</div>

Style-Property

besondere Syntax für die style-Property:

<div :style="{padding: '8px', margin: '8px'}">...</div>
<div :style="{color: completed ? 'grey' : 'black'}">
  ...
</div>

Events binden

Kurzform:

<button @click="alert('hello')">Say Hello</button>

Langform:

<button v-on:click="alert('hello')">Say Hello</button>

Events binden

Event-Modifier:

Verhindern des "Default-Verhaltens":

<form @submit.prevent="handleSubmit()">...</form>

Events binden

Zugriff auf das Event-Objekt mittes $event:

<button @click="handleClick($event)">handle event</button>

If / else

<div v-if="request.loading">Loading...</div>
<div v-else-if="request.error">Error while loading</div>
<div v-else>
  <h1>Results</h1>
  ...
</div>

Wiederholen von Elementen

<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{todo.title}}
  </li>
</ul>

Jedes wiederholte Element sollte eine lokal eindeutige key-Property haben (für Effizienz)

Two-way Binding für Inputs

Explizites Two-way Binding für Inputs:

<input :value="title" @input="title = $event.target.value" />

Kurzform:

<input v-model="firstName" />

Whitespace

Standardmäßig wird Whitespace zwischen Elementen nicht von Vue gerendert

<strong>no</strong> <em>space</em>

"Erzwingen" eines Leerzeichens:

<strong>with</strong>{{ " " }}<em>space</em>

Ãœbungen

Ãœbungen

Aufgaben:

  • Erstelle die Slideshow-Komponente nochmals (möglichst ohne auf alten Code zu blicken)
  • Erstelle eine Todo-List-Anwendung als einzelne Komponente

Ãœbung: Todo-Liste

Wir erstellen eine Todo-Anwendung mit der folgenden Funktionalität:

  • Anzeigen erledigter und nicht-erledigter Todos
  • Hinzufügen eines Todos mittels eines Formulars
  • Umschalten des erledigt-Zustandes eines Todos
  • Löschen eines Todos
  • Anzeigen der Anzahl an erledigten und nicht-erledigten Todos

Übung: Todo-Liste - teilweise Lösung

<div>
  <h1>Todo</h1>
  <form @submit.prevent="addTodo()">
    <input v-model="newTitle" />
    <button type="submit">add</button>
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.title }}
    </li>
  </ul>
</div>

Übung: Todo-Liste - teilweise Lösung

export default {
  name: 'TodoList',
  data() {
    return {
      newTitle: '',
      todos: [
        { id: 1, title: 'groceries', completed: false },
        { id: 2, title: 'taxes', completed: true },
      ],
    };
  },
  methods: {
    onSubmit() {
      this.addTodo();
      this.newTitle = '';
    },
    addTodo() {
      let maxId = 0;
      for (let todo of this.todos) {
        maxId = Math.max(maxId, todo.id);
      }
      this.todos.push({
        id: maxId + 1,
        title: this.newTitle,
        completed: false,
      });
    },
  },
};

Vue developer tools

Vue developer tools

Plugins für Chrome, Firefox und Edge

Funktionen:

  • Anzeigen der Komponentenstruktur
  • Anzeigen von State und Props der Komponenten
  • Ändern von State und Props
  • Loggen von Komponenten-Events
  • ...

Vue developer tools

mögliche Zugänge beim Suchen nach Problemen:

  • Ãœberprüfen der State-Update-Logik: State wird basierend auf ausgelösten Events korrekt aktualisiert
  • Ãœberprüfen der Rendering-Logik: State wird wie erwartet gerendert

VS Code

VS Code

https://code.visualstudio.com

Open-Source-Entwicklungsumgebung

Unabhängig vom eigentlichen Visual Studio

Grundlegendes

  • Öffnen eines Ordners
  • Datei-Explorer

Befehlspalette

F1 oder Ctrl + Shift + P: Befehlspalette

  • durchsuchbar
  • zeigt Kurzbefehle an

Befehlspalette

Beispiele für Befehle:

  • View: Toggle Terminal
  • Format Document
  • Find
  • Search: Find in Files
  • Preferences: Open Settings (UI)
  • Toggle line comment / Toggle block comment
  • Go to definition / Peek definition (nur für bestimmte Dateitypen)
  • Rename symbol (nur für bestimmte Dateitypen)

Konfiguration

Via File - Preferences - Settings

Eingeteilt in User Settings und Workspace Settings

Konfigurationsmöglichkeiten

Empfehlungen:

  • Accept Suggestion on Commit Character (Autovervollständigung ohne Enter): bei Verwendung von JavaScript / TypeScript deaktivieren
  • EOL: \n
  • Tab Size: 2

Weitere Möglichkeiten:

  • Auto Save
  • Format on Save
  • Word Wrap

VS Code Extensions für JavaScript und Vue

VS Code Extensions für JavaScript und Vue

Extensions-Sidebar öffnen: fünftes Symbol auf der linken Seite

mögliche Extensions:

  • Prettier (Code-Formatierung)
  • ESLint (Linter)
  • Vetur (Vue-Tooling)

Prettier

  • automatische Code-Formatierung nach strikten Regeln
  • für JavaScript, HTML, CSS
  • Tastenkürzel: Shift + Alt + F

ESLint

Linter mit mehr Funktionalität als der Standard-Linter von VS Code

Vue CLI

Vue CLI

Tool zum Initialisieren eines Vue-Projekts

Entwicklung mit node.js und npm

  • node.js: JS-Runtime
    • Ausführen des lokalen Enwicklungsservers
    • Unit-Tests
  • npm: Paketmanager
    • zum Verwalten von Abhängigkeiten
    • Pakete im node_modules-Ordner
    • Konfiguration in package.json

Vue CLI

Installieren und Ausführen:

npm install -g @vue/cli

vue create my-app

oder direktes Ausführen:

npx @vue/cli create my-app

Vue CLI

Erstellt eine einfache Vue-App, die als Ausgangspunkt verwendet werden kann

viele Aspekte können automatisch eingerichtet werden:

  • webpack und babel für den Build
  • lokaler Entwicklungsserver
  • Linter
  • TypeScript
  • Tests
  • CSS-Werkzeuge

Vue CLI

Konfiguration:

  • Version (2 oder 3)
  • TypeScript
  • Router
  • Vuex
  • CSS Pre-processors
  • Linter / Formatter (Build schlägt standardmäßig bei Linter-Fehlern fehl)
  • Unit Testing
  • E2E Testing

Vue CLI

damit der Build bei ESLint-Fehlern nicht fehlschlägt:

neue Datei vue.config.js:

module.exports = {
  lintOnSave: 'warning',
};

Vue CLI

Überprüfe Imports auf Fehler:

npm install eslint-plugin-import

in der eslint-Konfiguration unter "extends", füge "plugin:import/recommended" hinzu

Standard Projektstruktur

  • public/index.html, src/main.js: Einstiegspunkte
  • src/App.vue: definiert die App-Komponente
  • node_modules: Abhängigkeiten

Entwicklungsserver und Build

im Projektordner:

  • npm run serve: Startet den lokalen Entwicklungsserver
  • npm run build: Erstellt einen Build (zum Deployment)

Build und Deployment

Build und Deployment

Ein Vue-Projekt kann auf einem beliebigen statischen Hosting-Service gehostet werden

Build

build mit Vue CLI:

npm run build

Minifizierter Build wird im dist-Ordner erstellt

Deployment

einfache Möglichkeiten, um das Deployment zu testen (ohne Login):

Verwenden von Komponenten

Verwenden von Komponenten

Eigene Komponenten müssen im components-Eintrag der Elternkomponente aufgelistet werden:

import TodoItem from './TodoItem';
import AddTodo from './AddTodo';
export default {
  components: { TodoItem, AddTodo },
  // ...
};

Verwenden von Komponenten

Eigene Komponenten können im Template typischerweise auf zwei Arten geschrieben werden:

empfohlen:

<TodoItem />
<VBtn>foo</VBtn>

alternativ:

<todo-item />
<v-btn>foo</v-btn>

Komponenten-Libraries

Komponenten-Libraries

für Vue 2:

  • vuetify
  • bootstrap-vue
  • element-ui

für Vue 3:

  • vuetify-Release geplant für 2022-02
  • element-plus ist in der Beta-Phase

Vuetify 2: Setup

Hinzufügen von Vuetify 2 zu einem neuen Vue CLI-Projekt:

npx @vue/cli add vuetify

(Bemerkung: überschreibt App.vue und manche andere Dateien)

Vuetify 2: Setup

Stelle sicher, dass der ganze Komponentenbaum in App.vue von einer <v-app>-Komponente umfasst wird:

<template>
  <v-app>
    ...
  </v-app>
</template>

Vuetify 2: Komponenten

Beispiel für die Verwendung von Komponenten:

<v-btn color="primary" type="submit">add</v-btn>
<v-text-field v-model="newTitle" label="new title" />

Vuetify 2: App-Layout

<v-app>
  <v-app-bar app>
    <v-toolbar-title>Todo</v-toolbar-title>
  </v-app-bar>
  <v-main>
    ...
  </v-main>
  <v-footer app>Todo App by Marko</v-footer>
</v-app>

Vuetify 2: Container

v-container: responsive Container mit horizontalen Margins (Abständen)

<v-main>
  <v-container>
    ...
  </v-container>
</v-main>

Vuetify 2: Grid-System

Vuetify bietet ein Grid-System mit 12 Spalten (ähnlich wie bootstrap)

  • v-row: horizontaler Container, unterteilt in 12 Spalten
  • v-col: beinhaltet in v-row-Elementen

Vuetify 2: Grid-System

zwei Spalten mit gleicher Breite:

<v-row>
  <v-col>foo</v-col>
  <v-col>bar</v-col>
</v-row>

Vuetify 2: Grid-System

Konfigurieren der Breite:

<v-row>
  <v-col :cols="12" :sm="6" :md="3">
    <v-text-field v-model="newTitle" label="new title" />
  </v-col>
  <v-col>
    <v-btn color="primary" type="submit">Add</v-btn>
  </v-col>
</v-row>
  • :cols="12" - nutzt auf den kleinsten Bildschirmen alle 12 Spalten
  • :sm="6" - nutzt auf Bildschirmen der Mindestgröße small 6 Spalten
  • :md="3" - nutzt auf Bildschirmen der Mindestgröße medium 3 Spalten

Definieren eigener Komponenten

Definieren eigener Komponenten

mögliche Dateinamen für Komponenten-Dateien:

  • MyComponent.vue (empfohlen)
  • my-component.vue

Komponenten-Namen sollten immer mehrere Wörter sein (um sie von standard HTML-Elementen zu unterscheiden)

Definieren eigener Komponenten

Wiederholung: wichtige Properties in Komponentendefinitionen:

  • name
  • data
  • computed
  • methods

Definieren eigener Komponenten

eine Komponenten kann ein Interface definieren, mit dem sie mit ihrer Elternkomponente interagiert:

  • props: Daten, die nach unten übergeben werden
  • events: können aus einer Unterkomponente heraus ausgelöst werden

Komponenten-Props

State und Props

  • State = interner Zustand einer Komponente
  • Props = vom Elternelement übergebene Parameter

Komponenten-Props

Beispiele:

<ProgressBar percentage={75} color="lightgreen" />
<Rating :stars="3" />

Komponenten-Props

Props einer Komponente müssen in der Konfiguration aufgelistet werden:

export default {
  props: ['value', 'color'],
};

Zugriff: gleich wie für data, methods, ...

Komponenten-Props

Props aus mehreren Wörtern: üblicherweise mixedCase in JavaScript, kebab-case in Templates

props: ['greetingText']
<WelcomeMessage greeting-text="hi" />

Komponenten-Events

Datenfluss

  • parent → child: props
  • child → parent: events

Komponenten-Events

events have a name and potentially contain some data ("payload(s)")

Komponenten-Events

Beispiel: Verarbeiten von Komponenten-Events

<StarsRating
  :value="rating1"
  @update:value="rating1 = $event"
/>
<TodoItem
  :todo="todo"
  @delete="deleteTodo($event)"
  @update:completed="changeTodoCompleted($event)"
/>

Komponenten-Events

Auslösen eines Events aus einer Unterkomponente:

this.$emit('eventname', payload);

Komponenten-Events

Beispiel: StarsRating:

Komponenten und two-way-Binding

Komponenten und two-way-Binding

Wiederholung: Inputs:

explizites two-way-Binding für Inputs:

<input value="title" @input="title = $event.target.value" />

Kurzversion für two-way-Binding:

<input v-model="firstName" />

Komponenten und two-way-Binding

eigene Komponenten in Vue 3:

<StarsRating
  :modelValue="rating1"
  @update:modelValue="rating1 = $event"
/>

Kurzform:

<StarsRating v-model="rating1" />

Komponenten und two-way-Binding

eigene Komponenten in Vue 2:

<StarsRating :value="rating1" @input="rating1 = $event" />

Kurzform:

<StarsRating v-model="rating1" />

Eigene Komponenten: Ãœbungen

Eigene Komponenten: Ãœbungen

Aufgabe: Teile die Todo-Anwendung in kleinere Unterkomponenten auf (z.B. TodoList, TodoItem, AddTodo)

Aufgabe: Extrahiere wiederverwendbare Komponenten, die hauptsächlich für das Styling verwendet werden - z.B. eine Button-Komponente oder eine TextInput-Komponente

Inhalte an Komponenten übergeben: Slots

Inhalte an Komponenten übergeben: Slots

Wir können Komponenten erstellen, die wie folgt verwendet werden können:

<DialogModal type="error">
  <p>Changes could not be saved</p>
  <button>OK</button>
</DialogModal>

Inhalte an Komponenten übergeben: Slots

Template für DialogModal:

<div :class="{dialog: true, dialogType: type}">
  <slot></slot>
</div>

Composition API

Composition API vs Options API

Composition API: Komponentenlogik wird in einer setup-Methode definiert

Composition API verglichen mit dem Options API:

  • bessere Unterstützung für TypeScript
  • modularer (Logik kann aus der Komponentendefinition extrahiert werden)
  • etwas verboser

Composition API vs Options API

options API:

export default {
  name: 'TodoApp',
  data() {
    return {
      todos: [],
      newTitle: '',
    };
  },
  methods: {
    /*...*/
  },
  computed: {
    /*...*/
  },
};

Composition API vs options API

composition API:

import { ref, reactive, computed } from 'vue';
export default {
  name: 'TodoApp',
  setup() {
    const todos = reactive([]);
    const newTitle = ref('');
    function addTodo() {
      // ...
    }
    const numActive = computed(
      () => todos.filter((t) => !t.completed).length
    );
    return { todos, newTitle, addTodo, numActive };
  },
};

Composition API vs options API

mögliche Umstrukturierung des Codes:

function useTodos() {
  // manages todos
}

export default {
  name: 'TodoApp',
  setup() {
    const newTitle = ref('');
    const { todos, addTodo, numActive } = useTodos();
    return { todos, addTodo, numActive, newTitle };
  },
};

Composition API

VueUse: Sammlung von vordefinierten Vue Composition-Funktionen:

https://github.com/vueuse/vueuse

Beispiele:

  • useFetch
  • useGeolocation
  • useMediaQuery
  • useNow
  • useOnline
  • ...

State im composition API

zwei Mechanismen:

  • ref für primitive / unveränderbare Daten (string, number, ...)
  • reactive für Objekte, Arrays, ...

refs

Initialisierung eines refs:

const newTitle = ref('');

Lesen / Schreiben aus dem Skript:

const a = newTitle.value;
newTitle.value = 'foo';

Lesen / Schreiben aus dem Template:

<div>new title is {{newTitle}}</div>
<button @click="newTitle = 'foo'">set to foo</button>

Beispiel: Komponentendefinition (todo app)

<div>
  <h1>Todo</h1>
  <form @submit.prevent="addTodo()">
    <input v-model="newTitle" />
    <button type="submit">add</button>
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.title }}
    </li>
  </ul>
</div>

Beispiel: Komponentendefinition (todo app)

import { ref, reactive } from 'vue';
export default {
  name: 'TodoApp',
  setup() {
    const newTitle = ref('');
    const todos = reactive([
      { id: 1, title: 'groceries', completed: false },
      { id: 2, title: 'taxes', completed: true },
    ]);
    function onSubmit() {
      addTodo(newTitle.value);
      newTitle.value = '';
    }
    function addTodo(title) {
      const maxId = Math.max(0, ...todos.map((t) => t.id));
      todos.push({
        id: newId,
        title: newTitle.value,
        completed: false,
      });
    }
    return { newTitle, todos, onSubmit };
  },
};

Ãœbung

Erstelle eine Slideshow-Komponente / -Anwendung, die Bilder wie das folgende darstellt:

https://picsum.photos/300/200?image=0

Composition API: Props und Events

Props und Events

Die setup-Methode erhält zwei Argumente: props und context

Beispiel: Rating-Komponente:

export default {
  name: 'StarsRating',
  props: ['value'],
  setup(props, context) {
    const ariaLabel = computed(
      () => `${props.value} out of 5 stars`
    );
    const onStarClick = (id) => {
      context.emit('change', id);
    };
    return { ariaLabel, onStarClick };
  },
};

Netwerkanfragen in JavaScript

Netzwerkanfragen in JavaScript

Promises: moderne Möglichkeit, asynchronen Code zu verwenden:

  • Promises mit async / await
  • Promises mit .then()

moderne Möglichkeiten, um Netzwerkanfragen zu senden:

  • fetch() (in Browsern inkludiert)
  • axios (library)

Netzwerkanfragen in JavaScript

asynchrone Funktion, die Todos von einem API lädt:

// todosApi.ts
async function fetchTodos(): Promise<Array<Todo>> {
  const url = 'https://jsonplaceholder.typicode.com/todos';
  const res = await fetch(url);
  const apiTodos = await res.json();
  // convert data format (don't include userId)
  const todos = apiTodos.map((todo) => ({
    id: todo.id,
    title: todo.title,
    completed: todo.completed,
  }));
  return todos;
}

Netzwerkanfragen in JavaScript

Asynchrone Funktion, die Wechselkursdaten von einem API lädt:

async function fetchExchangeRate(
  from: string,
  to: string
): Promise<number> {
  const res = await fetch(
    `https://api.exchangerate.host/convert?from=${from}&to=${to}`
  );
  const data = await res.json();
  return data.result;
}
}

Side Effects

Side Effects

Wenn sich Komponenten-Props bzw State ändern:

"Main Effect": Komponente wir mit aktuellen Daten (neu) gerendert

mögliche "Side Effects": Auslösen von API-Abfragen, Speichern von Daten, Explizite Änderungen am DOM, ...

Side Effects

Typischerweise möchten wir Side Effects auslösen, wenn sich bestimmte Props oder State geändert haben oder wenn die Komponente zum ersten Mal eingebunden wurde

Side Effects

typische Fälle von Side Effects:

  • Auslösen von API-Anfragen
    • wenn eine Komponente zum ersten Mal eingebunden wird
    • wenn sich bestimmte Daten (State / Props) geändert haben (z.B. wenn der Benutzer ein bestimmtes Element auswählt, um dessen Details zu sehen)
  • Speichern von Daten in localStorage, wenn sie sich geändert haben
  • Starten von Timern
  • ...

Side Effects

typische Auslöser für Side Effects:

  • beim ersten Rendern einer Komponente
  • wenn sich Einträge in state / props ändern

Side Effects

Side Effects im Options API:

  • Lifecycle-Event: created
  • beim Ändern von Daten: watch-Funktion

Side Effects im Composition API (Variante 1):

  • Initialisierung in setup
  • wenn sich Daten Ändern: watch-Funktion

Side Effects im Composition API (Variante 2):

  • watchEffect-Funktion: Läuft beim ersten Rendering und bei Änderungen von überwachten Werten

Side Effects: Lifecycle Events

Side Effects: Lifecycle Events

Options API: Komponentenmethoden, die bei bestimmten Lifecycle-Events aufgerufen werden:

  • created
  • mounted
  • updated
  • destroyed
  • ...

Side Effects: Lifecycle Events

Composition API: äquivalente Funktionen:

  • created → aufnehmen in setup
  • mounted → onMounted
  • updated → onUpdated
  • destroyed → onUnmounted
  • ...

Side Effects: Lifecycle Events

Laden von Daten beim ersten Einbinden einer Komponente (Options API):

export default {
  // ...
  async created() {
    const res = await fetch(
      'https://jsonplaceholder.typicode.com/todos'
    );
    const todos = await res.json();
    this.todos = todos;
  },
};

Side effects: watchEffect

watchEffect

Die watchEffect-Funktion kann verwendet werden, um side effects auszulösen, wenn die Komponente zum ersten Mal eingebunden wird oder wenn sich Props oder State geändert haben

watchEffect

Beispiel: Laden von SpaceX-Startdaten beim ersten Einbinden, oder wenn sich launchNr geändert hat

function setup() {
  const launchNr = ref(1);
  const loading = ref(true);
  const launchData = reactive({ name: null, date: null });

  watchEffect(async () => {
    loading.value = true;
    const res = await fetch(
      `https://api.spacexdata.com/v3/launches/${launchNr.value}`
    );
    const data = await res.json();
    loading.value = false;
    launchData.name = data.mission_name;
    launchData.date = data.launch_date_utc;
  });

  return { launchNr, launchData };
}

watchEffect

Ãœbungen:

  • Laden und Darstellen von mehr Daten
  • Hinzufügen eines Ladeindikators

watchEffect

vollständiger Code für eine SpaceX-Launch-Komponente:

<template>
  <article>
    <button @click="launchNr--">prev</button>
    <button @click="launchNr++">next</button>
    <div v-if="loading">loading...</div>
    <div v-else>
      <h1>{{ launchData.name }}</h1>
      <p>{{ launchData.date }}</p>
      <img :src="launchData.patch" :key="Math.random()" />
    </div>
  </article>
</template>

<script>
import { reactive, ref, watchEffect } from 'vue';
export default {
  setup() {
    const launchNr = ref(1);
    const loading = ref(true);
    const launchData = reactive({
      name: null,
      date: null,
      patch: '',
    });

    watchEffect(async () => {
      loading.value = true;
      const res = await fetch(
        `https://api.spacexdata.com/v3/launches/${launchNr.value}`
      );
      const data = await res.json();
      loading.value = false;
      launchData.name = data.mission_name;
      launchData.date = data.launch_date_utc;
      launchData.patch = data.links.mission_patch_small;
    });
    return { launchNr, launchData, loading };
  },
};
</script>

watchEffect

Laden aus / Speichern in localStorage (Counter-Komponente):

import { ref, watchEffect } from 'vue';
export default {
  setup() {
    // try to load
    const countStored = Number(
      localStorage.getItem('count')
    );
    const count = ref(countStored || 0);
    // persist to localStorage if count changes
    watchEffect(() => {
      localStorage.setItem('count', count.value);
    });
    return { count };
  },
};