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
Inhaltsverzeichnis
- Einführung
- Performance Profiling
- Optimierung des Seitenladevorgangs
- Update-Optimierungen
- 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.
<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 oderv-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.
<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:
<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.