Zum Hauptinhalt springen

[Lv3] Nuxt 3 Performance-Optimierung: Bundle Size, SSR-Geschwindigkeit und Bildoptimierung

Komplettleitfaden zur Nuxt 3 Performance-Optimierung: Von der Bundle-Size-Verkleinerung über SSR-Geschwindigkeitsoptimierung bis hin zu Bildladestrategien für maximale Performance.


1. Hauptachse der Interview-Antwort

  1. Bundle Size Optimierung: Analyse (nuxi analyze), Aufteilung (SplitChunks), Tree Shaking, Lazy Loading.
  2. SSR-Geschwindigkeitsoptimierung (TTFB): Redis Cache, Nitro Cache, Reduzierung blockierender API-Aufrufe, Streaming SSR.
  3. Bildoptimierung: @nuxt/image, WebP-Format, CDN, Lazy Loading.
  4. Optimierung großer Datenmengen: Virtual Scrolling, Infinite Scroll, Pagination.

2. Wie reduziert man die Bundle Size von Nuxt 3?

2.1 Diagnosewerkzeuge

Zunächst muss man wissen, wo der Engpass liegt. Verwenden Sie nuxi analyze, um die Bundle-Struktur zu visualisieren.

npx nuxi analyze

Dies erzeugt einen Bericht, der zeigt, welche Pakete am meisten Platz einnehmen.

2.2 Optimierungsstrategien

1. Code Splitting

Nuxt 3 macht standardmäßig bereits routenbasiertes Code Splitting. Bei großen Paketen (wie ECharts, Lodash) müssen wir manuell optimieren.

Nuxt Config Konfiguration (Vite/Webpack):

// nuxt.config.ts
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('lodash')) return 'lodash';
if (id.includes('echarts')) return 'echarts';
}
},
},
},
},
},
});

2. Tree Shaking und bedarfsgesteuerte Imports

Stellen Sie sicher, dass nur benötigte Module importiert werden, nicht das gesamte Paket.

// Falsch: gesamtes lodash importieren
import _ from 'lodash';
_.debounce(() => {}, 100);

// Richtig: nur debounce importieren
import debounce from 'lodash/debounce';
debounce(() => {}, 100);

// Empfohlen: vueuse verwenden (Vue-spezifisch und tree-shakable)
import { useDebounceFn } from '@vueuse/core';

3. Komponenten-Lazy Loading

Für Komponenten, die nicht auf der ersten Seite benötigt werden, das Lazy-Präfix für dynamische Imports verwenden.

<template>
<div>
<!-- Der Komponentencode wird erst geladen, wenn show true ist -->
<LazyHeavyComponent v-if="show" />
</div>
</template>

4. Unnötige Server-side-Pakete entfernen

Stellen Sie sicher, dass Pakete, die nur auf dem Server verwendet werden (Datenbanktreiber, fs-Operationen), nicht in den Client gebündelt werden. Nuxt 3 behandelt automatisch Dateien mit .server.ts-Endung oder das server/-Verzeichnis.


3. Wie optimiert man die SSR-Geschwindigkeit (TTFB)?

3.1 Warum ist der TTFB zu lang?

TTFB (Time To First Byte) ist der Schlüsselindikator für SSR-Performance. Häufige Ursachen:

  1. Langsame API-Antworten: Der Server muss auf Backend-API-Antworten warten, bevor HTML gerendert werden kann.
  2. Serielle Anfragen: Mehrere API-Anfragen werden nacheinander statt parallel ausgeführt.
  3. Aufwändige Berechnungen: Server führt zu viele CPU-intensive Aufgaben aus.

3.2 Optimierungslösungen

1. Server-Side Caching (Nitro Cache)

Nitro's Cache-Funktionalität nutzen, um API-Antworten oder Rendering-Ergebnisse zwischenzuspeichern.

// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/': { swr: 3600 },
'/products/**': { swr: 600 },
'/api/**': { cache: { maxAge: 60 } },
},
});

2. Parallele Anfragen (Parallel Fetching)

Promise.all verwenden, um mehrere Anfragen parallel zu senden.

// Langsam: serielle Ausführung (Gesamtzeit = A + B)
const { data: user } = await useFetch('/api/user');
const { data: posts } = await useFetch('/api/posts');

// Schnell: parallele Ausführung (Gesamtzeit = Max(A, B))
const [{ data: user }, { data: posts }] = await Promise.all([
useFetch('/api/user'),
useFetch('/api/posts'),
]);

3. Nicht-kritische Daten verzögern (Lazy Fetching)

Daten, die nicht auf der ersten Seite benötigt werden, können auf dem Client geladen werden (lazy: true).

const { data: comments } = await useFetch('/api/comments', {
lazy: true,
server: false,
});

4. Streaming SSR (experimentell)

Nuxt 3 unterstützt HTML Streaming: gleichzeitig rendern und senden.


4. Nuxt 3 Bildoptimierung

4.1 Verwendung von @nuxt/image

Das offizielle Modul @nuxt/image ist die optimale Lösung:

  • Automatische Formatkonvertierung: automatisch zu WebP/AVIF.
  • Automatische Skalierung: erzeugt Bilder in passender Größe.
  • Lazy Loading: eingebautes Lazy Loading.
  • CDN-Integration: unterstützt Cloudinary, Imgix und andere Provider.

4.2 Implementierungsbeispiel

npm install @nuxt/image
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
format: ['webp'],
},
});
<template>
<NuxtImg
src="/hero.jpg"
format="webp"
width="300"
loading="lazy"
placeholder
/>
</template>

5. Paginierung und Scrollen bei großen Datenmengen

5.1 Lösungsauswahl

Bei großen Datenmengen (z.B. 10.000 Produkte) gibt es drei Hauptstrategien unter Berücksichtigung von SEO:

StrategieGeeignetes SzenarioSEO-Kompatibilität
Traditionelle PaginierungE-Commerce-Listen, ArtikellistenAusgezeichnet (am besten)
Unendliches ScrollenSoziale Feeds, FotogalerienGering (Sonderbehandlung nötig)
Virtual ScrollKomplexe Berichte, sehr lange ListenSehr gering (Inhalt nicht im DOM)

5.2 SEO bei unendlichem Scrollen beibehalten

Bei unendlichem Scrollen crawlen Suchmaschinen normalerweise nur die erste Seite. Lösungen:

  1. Kombination mit Paginierung: <link rel="next" href="..."> Tags bereitstellen.
  2. Noscript Fallback: Traditionelle Paginierung in <noscript> für Crawler.
  3. "Mehr laden"-Button: SSR rendert die ersten 20 Einträge; weitere Klicks auf "Mehr laden" oder Scrollen lösen Client-side Fetch aus.

6. Lazy Loading in SSR-Umgebung

6.1 Problembeschreibung

In SSR-Umgebungen führt die Verwendung von IntersectionObserver für Lazy Loading zu Fehlern oder Hydration Mismatch, da der Server kein window oder document hat.

6.2 Lösungen

1. Nuxt-eigene Komponenten verwenden

  • <LazyComponent>
  • <NuxtImg loading="lazy">

2. Benutzerdefinierte Directive (mit SSR-Behandlung)

// plugins/lazy-load.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('lazy', {
mounted(el, binding) {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
el.src = binding.value;
observer.disconnect();
}
});
observer.observe(el);
},
getSSRProps(binding) {
return {
src: 'placeholder.png'
};
}
});
});

7. SSR Performance-Monitoring und Tracking

7.1 Warum ist Monitoring nötig?

Performance-Engpässe von SSR-Anwendungen treten oft auf dem Server auf, unsichtbar für die Browser-DevTools.

7.2 Gängige Werkzeuge

  1. Nuxt DevTools (Entwicklungsphase): Server Routes Antwortzeiten anzeigen.
  2. Lighthouse / PageSpeed Insights (nach dem Deployment): Core Web Vitals (LCP, CLS, FID/INP) überwachen.
  3. Server-Side Monitoring (APM): Sentry / Datadog, OpenTelemetry.

7.3 Einfache Zeitmessung implementieren

// server/middleware/timing.ts
export default defineEventHandler((event) => {
const start = performance.now();

event.node.res.on('finish', () => {
const duration = performance.now() - start;
console.log(`[${event.method}] ${event.path} - ${duration.toFixed(2)}ms`);
});
});

8. Interview-Zusammenfassung

F: Wie verfolgt und überwacht man SSR-Performance-Probleme?

In der Entwicklungsphase verwende ich hauptsächlich Nuxt DevTools. In der Produktionsumgebung überwache ich Core Web Vitals (besonders LCP) und TTFB. Für tiefgehende Server-Analyse verwende ich benutzerdefinierte Server Middleware oder integriere Sentry / OpenTelemetry.

F: Wie reduziert man die Nuxt 3 Bundle Size?

Zuerst analysiere ich mit nuxi analyze. Für große Pakete wende ich Tree Shaking oder manuelle Aufteilung (manualChunks) an. Für nicht-erstseiten-relevante Komponenten verwende ich <LazyComponent>.

F: Wie optimiert man die SSR-Geschwindigkeit?

Fokus auf TTFB-Reduzierung. Nitro routeRules für Server-side Caching (SWR). API-Anfragen mit Promise.all parallelisieren. Nicht-kritische Daten mit lazy: true auf den Client verlagern.

F: Wie macht man Bildoptimierung?

Ich verwende das @nuxt/image-Modul für automatische WebP-Konvertierung, automatische Skalierung und Lazy Loading.

F: Wie erhält man SEO bei unendlichem Scrollen?

Unendliches Scrollen ist nicht SEO-freundlich. Bei Inhaltsseiten bevorzuge ich traditionelle Paginierung. Wenn unendliches Scrollen nötig ist, rendere ich die erste Seite mit SSR und verwende Meta Tags (rel="next") oder Noscript-Paginierungslinks.