Performance von Vue-Anwendungen verbessern | Best Practices

Lerne, wie du die Performance deiner Vue-Apps optimieren kannst. Ich zeige dir Best Practices, um Ladezeiten zu verkürzen und die UX zu steigern.

7. September 2024

Nico Meyer
Nico Meyer
Fullstack JavaScript Entwickler

Inhaltsverzeichnis

  1. Einführung
  2. Performance Profiling
  3. Optimierung des Seitenladevorgangs
  4. Update-Optimierungen
  5. Generelle Optimierungen

Einführung

Vue ist so konzipiert, dass es für die meisten Anwendungsfälle performant ist, ohne dass erhebliche manuelle Optimierungen erforderlich sind. Dennoch gibt es Szenarien, in denen zusätzliche Feinabstimmungen notwendig sind.

Performance Profiling

Bevor wir mit der Optimierung beginnen, müssen wir die Performance messen. Hier sind ein paar nützliche Tools:

  • PageSpeed Insights
  • WebPageTest
  • Chrome DevTools Performance Panel
  • Vue DevTools Extension

Für Vue-spezifische Leistungsmarker kann app.config.performance aktiviert werden.

Optimierung des Seitenladevorgangs

Architekturwahl

Für Seiten, die äußerst schnell laden sollen, sollte man Server-Side Rendering (SSR) oder Static Site Generation (SSG) in Betracht ziehen. Reine Client-Side SPAs (Single Page Applications) haben oft langsame Ladezeiten. Marketingseiten (z.B. Landingpages) können separat als statisches HTML bereitgestellt werden.

Bundle Size Reduktion und Tree-shaking

Eine der effektivsten Methoden zur Optimierung der Ladegeschwindigkeit ist die Reduzierung der JavaScript-Bundle-Größe:

  • Verwende moderne Build-Tools, die "tree-shaking" unterstützen.
  • Achte auf die Größe neuer Abhängigkeiten.
  • Verwende ES-Module, wenn möglich.
// Beispiel für tree-shaking mit dynamic import
import { defineAsyncComponent } from 'vue';

const Foo = defineAsyncComponent(() => import('./Foo.vue'));

Code Splitting

Beim Code Splitting wird das Anwendungspaket in mehrere kleinere Teile aufgeteilt, die nach Bedarf geladen werden können. Dies ermöglicht eine schnellere Ladegeschwindigkeit für die initiale Seite.

function loadLazy() {
  return import('./lazy.js');
}

Update-Optimierungen

Props Stabilität

Eine Child-Komponente wird nur aktualisiert, wenn sich mindestens eines ihrer Props geändert hat. Es ist wichtig, die Stabilität der Props zu gewährleisten, um unnötige Updates zu vermeiden.

ChildComponent.vue
<template>
  <div :class="{ active: isActive }">...</div>
</template>

<script lang="ts" setup>
defineProps<{ id: number, active: boolean }>();
</script>

Verwendung von v-once und v-memo

  • v-once: Kann verwendet werden, um Inhalte zu rendern, die zur Laufzeit auf Daten basieren, aber nie aktualisiert werden müssen.
  • v-memo: Kann verwendet werden, um bedingt die Aktualisierung großer Sub-Bäume oder v-for-Listen zu überspringen.
<template>
  <div v-once>
    {{ neverChangingContent }}
  </div>
</template>

Stabilität von Berechnungen

Ab Vue 3.4 wird eine berechnete Eigenschaft nur dann Effekte auslösen, wenn sich ihr Wert geändert hat.

const count = ref(0);
const isEven = computed(() => count.value % 2 === 0);

Generelle Optimierungen

Virtualisierung großer Listen

Große Listen sollten virtualisiert werden, um die Performance zu verbessern. Dabei werden nur die Elemente gerendert, die in der Nähe des Viewports sind.

Verfügbare Bibliotheken:

  • vue-virtual-scroller
  • vue-virtual-scroll-grid
  • vueuc/VVirtualList

Reduzierung der Reaktivität für große, unveränderliche Strukturen

Vue's Reaktivitätssystem ist standardmäßig tief. Für große, tief verschachtelte Arrays kann dies zu einem Performance-Overhead führen. Verwende shallowRef() und shallowReactive(), um nur auf Root-Ebene reaktiv zu sein.

const shallowArray = shallowRef([/* große Liste von tiefen Objekten */]);

Vermeidung unnötiger Komponentenabstraktionen

Manchmal erstellst du renderless Components oder Higher-Order Components, um eine bessere Abstraktion oder Code-Organisation zu erreichen. Renderless Components sind Komponenten, die keine eigenen DOM-Elemente erzeugen, sondern nur Funktionen und Verhalten für ihre Kindkomponenten bereitstellen. Higher-Order Components sind Komponenten, die andere Komponenten mit zusätzlichen Props rendern.

Obwohl diese Muster nützlich und oft notwendig sind, solltest du dir der Tatsache bewusst sein, dass Komponenteninstanzen viel teurer sind als einfache DOM-Knoten. Jede Komponenteninstanz bringt einen gewissen Overhead mit sich, der in Bezug auf Speicherverbrauch und Performance aufwendig sein kann.

Wenn du diese Muster übermäßig verwendest, insbesondere in Performance-kritischen Bereichen wie großen Listen, können die Kosten in Form von längeren Renderzeiten und erhöhtem Speicherverbrauch spürbar werden.

Beispiel und Kontext:

Stelle dir eine Liste mit 100 Elementen vor, wobei jedes Element eine eigene Komponenteninstanz und mehrere Kindkomponenten enthält.

ChildComponent.vue
<template>
  <div v-for="item in items" :key="item.id">
    <ItemComponent :item="item" />
  </div>
</template>

<script lang="ts" setup>
const items = ref([...]);
</script>

In dieser Situation entstehen hunderte von Komponenteninstanzen, die alle ihren eigenen internen Zustand und Lebenszyklusmethoden haben.

Durch das Entfernen unnötiger Komponentenabstraktionen kannst du die Anzahl dieser Instanzen erheblich reduzieren:

ChildComponent.vue
<template>
  <div v-for="item in items" :key="item.id">
    <div :class="{ active: item.isActive }">{{ item.name }}</div>
  </div>
</template>

<script lang="ts" setup>
const items = ref([...]);
</script>

Wann ist eine Optimierung sinnvoll?

Es ist wichtig zu beachten, dass das Entfernen nur weniger Komponenteninstanzen keine merkliche Auswirkung hat. Wenn eine Komponente nur ein paar Mal in der gesamten Anwendung gerendert wird, sind die Performance-Kosten vernachlässigbar.

Die beste Gelegenheit für diese Optimierung sind große Listen oder stark genutzte Bereiche der Anwendung. Nehmen wir an, eine Liste besteht aus 100 Elementen und jedes Element enthält mehrere Kindkomponenten. Durch das Entfernen einer überflüssigen Abstraktionskomponente könnte sich die Gesamtzahl der zu erstellenden Komponenteninstanzen um mehrere hundert verringern.

Das Ziel ist es, die richtige Balance zwischen Code-Organisation und Performance zu finden und zu erkennen, wann die Abstraktion Nachteile in Bezug auf Leistungsfähigkeit mit sich bringt.