Skip to content

Kapitel 13: Erweiterte Konfiguration & Optimierung

📖 Lernziele

In diesem Kapitel lernen Sie:

  • next.config.js erweitert konfigurieren
  • ✅ Performance-Optimierungstechniken anwenden
  • ✅ Benutzerdefinierte Middleware erstellen
  • ✅ Fehlerbehandlung implementieren
  • ✅ Internationalisierung (i18n) einrichten

13.1 Erweiterte next.config.js Konfiguration

⚙️ Grundkonfiguration (Überblick)

javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  
  // Bilder-Konfiguration
  images: {
    domains: ['example.com', 'via.placeholder.com'],
    formats: ['image/webp'],
  },
  
  // Umgebungsvariablen (nicht für sensible Daten!)
  env: {
    CUSTOM_KEY: 'mein-wert',
  },
  
  // Webpack-Konfiguration (falls nötig)
  webpack: (config, { isServer }) => {
    // Hier können Sie Webpack anpassen
    return config;
  },
};

module.exports = nextConfig;

🔄 Rewrites (Proxy)

Zweck: Anfragen an eine andere URL weiterleiten (CORS-Umgehung).

javascript
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'https://api.example.com/:path*',
      },
    ];
  },
};

Verwendung:

javascript
// Anfrage an /api/users wird zu https://api.example.com/users weitergeleitet
const res = await fetch('/api/users');

🔀 Redirects

Zweck: Permanente oder temporäre Weiterleitungen.

javascript
const nextConfig = {
  async redirects() {
    return [
      {
        source: '/alt',
        destination: '/neu',
        permanent: true, // 301 Redirect (SEO-freundlich)
      },
      {
        source: '/temporär',
        destination: '/neu',
        permanent: false, // 302 Redirect
      },
    ];
  },
};

📦 Bundle-Analyse

Installation:

bash
pnpm add -D @next/bundle-analyzer

Konfiguration (next.config.js):

javascript
const withBundleAnalyzer = require('@next/bundle-analyzer');

const nextConfig = withBundleAnalyzer({
  reactStrictMode: true,
  // ... andere Konfiguration
});

module.exports = nextConfig;

Verwendung:

bash
ANALYZE=true npm run build

13.2 Performance-Optimierung

⚡ Code-Splitting (Automatisch)

Next.js führt automatisches Code-Splitting durch (jede Seite lädt nur ihren eigenen JavaScript-Code).

Manuelles dynamisches Importieren:

jsx
import dynamic from 'next/dynamic';

// Dynamisches Importieren (Lazy Loading)
const DynamicComponent = dynamic(() => import('@/components/HeavyComponent'), {
  loading: () => <p>Lädt...</p>,
  ssr: false, // Nur Client-seitig (z.B. für Chart-Bibliotheken)
});

export default function HomePage() {
  return (
    <div>
      <h1>Startseite</h1>
      <DynamicComponent />
    </div>
  );
}

🖼️ Bildoptimierung (next/image)

Vorteile:

  • ✅ Automatische WebP-Konvertierung
  • ✅ Lazy Loading
  • ✅ Größenanpassung

Beispiel:

jsx
import Image from 'next/image';
import logo from '@/public/logo.png';

export default function HomePage() {
  return (
    <div>
      {/* Lokales Bild */}
      <Image
        src={logo}
        alt="Logo"
        width={200}
        height={100}
        priority // Für kritische Bilder (Above the Fold)
      />
      
      {/* Remote Bild */}
      <Image
        src="https://example.com/bild.jpg"
        alt="Remote Bild"
        width={500}
        height={300}
        loading="lazy" // Standard (Lazy Loading)
      />
    </div>
  );
}

next.config.js für Remote-Bilder:

javascript
const nextConfig = {
  images: {
    domains: ['example.com', 'via.placeholder.com'],
    // Oder für alle Domains (Next.js 14+)
    remotePatterns: [
      {
        protocol: 'https',
        hostname: '**.example.com',
      },
    ],
  },
};

🔤 Font-Optimierung (next/font)

Google Fonts:

jsx
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({ children }) {
  return (
    <html lang="de" className={inter.className}>
      <body>{children}</body>
    </html>
  );
}

Lokale Fonts:

javascript
import localFont from 'next/font/local';

const myFont = localFont({
  src: [
    {
      path: './fonts/my-font.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './fonts/my-font-bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-my-font',
});

export default function RootLayout({ children }) {
  return (
    <html lang="de" className={myFont.variable}>
      <body>{children}</body>
    </html>
  );
}

📦 Caching-Strategien

Fetch-Caching (Server Components):

jsx
// 1. Standard (gecacht)
const res = await fetch('https://api.example.com/data');

// 2. Kein Cache (immer aktuell)
const res = await fetch('https://api.example.com/data', { cache: 'no-store' });

// 3. Revalidierung (alle 60 Sekunden)
const res = await fetch('https://api.example.com/data', { next: { revalidate: 60 } });

ISR (Incremental Static Regeneration):

jsx
// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(res => res.json());
  
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export default async function BlogPost() {
  // ISR: Alle 60 Sekunden aktualisieren
  const res = await fetch('https://api.example.com/posts', { next: { revalidate: 60 } });
  // ...
}

13.3 Benutzerdefinierte Middleware

🎯 Was ist Middleware?

Middleware führt Code vor dem Laden einer Seite aus (z.B. für Auth-Prüfung, Weiterleitungen).

📝 Middleware erstellen

Datei: middleware.js (im Projekt-Root)

javascript
import { NextResponse } from 'next/server';

export function middleware(request) {
  // Beispiel: Auth-Prüfung
  const token = request.cookies.get('token');
  
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  // Weiterleitung basierend auf Sprache
  const locale = request.cookies.get('locale')?.value || 'de';
  if (request.nextUrl.pathname === '/') {
    return NextResponse.redirect(new URL(`/${locale}`, request.url));
  }
}

// Welche Routen sollen abgefangen werden?
export const config = {
  matcher: ['/dashboard/:path*', '/'],
};

🌐 Anwendungsfall: Sprachweiterleitung

javascript
import { NextResponse } from 'next/server';

export function middleware(request) {
  // Sprache aus Header ermitteln
  const acceptLanguage = request.headers.get('accept-language') || 'de';
  const locale = acceptLanguage.split(',')[0].split('-')[0];
  
  // Weiterleitung, wenn keine Sprache in der URL
  if (request.nextUrl.pathname === '/') {
    return NextResponse.redirect(new URL(`/${locale}`, request.url));
  }
}

export const config = {
  matcher: ['/'],
};

13.4 Fehlerbehandlung

📝 error.js (Fehlerbehandlung pro Route)

Datei: app/error.js (oder in einem Unterordner)

jsx
'use client';

export default function Error({ error, reset }) {
  return (
    <div style={{ textAlign: 'center', padding: '100px' }}>
      <h1>Etwas ist schiefgegangen!</h1>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Erneut versuchen</button>
    </div>
  );
}

📝 not-found.js (404-Seite)

Datei: app/not-found.js

jsx
import Link from 'next/link';

export default function NotFound() {
  return (
    <div style={{ textAlign: 'center', padding: '100px' }}>
      <h1>404 - Seite nicht gefunden</h1>
      <p>Die angeforderte Seite existiert nicht.</p>
      <Link href="/">Zur Startseite</Link>
    </div>
  );
}

🛠️ Globale Fehlerbehandlung (API-Routen)

Beispiel: app/api/route.js

javascript
export async function GET() {
  try {
    const data = await fetchData();
    return Response.json(data);
  } catch (error) {
    console.error('API-Fehler:', error);
    return Response.json(
      { error: 'Interner Serverfehler' },
      { status: 500 }
    );
  }
}

13.5 Internationalisierung (i18n)

⚠️ Next.js 14: i18n wird durch Middleware ersetzt

In Next.js 14 (App Router) wird keine eingebaute i18n-Unterstützung mehr angeboten. Stattdessen verwendet man Middleware oder Bibliotheken wie next-intl.

🌍 Einfache Sprachunterstützung (Middleware)

middleware.js:

javascript
import { NextResponse } from 'next/server';

const locales = ['de', 'en', 'fr'];
const defaultLocale = 'de';

export function middleware(request) {
  const pathname = request.nextUrl.pathname;
  
  // Wenn die URL bereits eine Sprache enthält
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );
  
  if (pathnameHasLocale) return NextResponse.next();
  
  // Weiterleitung zur Standardsprache
  return NextResponse.redirect(
    new URL(`/${defaultLocale}${pathname}`, request.url)
  );
}

export const config = {
  matcher: ['/((?!_next|favicon.ico).*)'],
};

Verzeichnisstruktur:

app/
├── [locale]/
│   ├── layout.js
│   ├── page.js
│   └── about/
│       └── page.js

app/[locale]/layout.js:

jsx
export default function LocaleLayout({ children, params }) {
  const { locale } = params;
  
  return (
    <html lang={locale}>
      <body>
        {children}
      </body>
    </html>
  );
}

📚 next-intl Bibliothek (Empfohlen)

Installation:

bash
pnpm add next-intl

Konfiguration: Folgen Sie der offiziellen Dokumentation.


📝 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

KonzeptErklärung
next.config.jsRewrites, Redirects, Bundle-Analyse
PerformanceCode-Splitting, Bildoptimierung, Font-Optimierung
CachingFetch-Caching, ISR
MiddlewareAuth-Prüfung, Sprachweiterleitung
Fehlerbehandlungerror.js, not-found.js
i18nInternationalisierung mit Middleware

✅ Nächste Schritte

  1. Übung: Konfigurieren Sie next.config.js mit Rewrites
  2. Übung: Implementieren Sie eine Middleware für Auth-Prüfung
  3. Weiter geht's: Kapitel 14 - Deployment & Live-Schaltung

🎯 Selbsttest

Frage 1: Wie konfiguriert man Rewrites in next.config.js?

Antwort anzeigen Durch Hinzufügen der `async rewrites()` Funktion zur Konfiguration.

Frage 2: Was ist der Vorteil von next/image?

Antwort anzeigen Automatische WebP-Konvertierung, Lazy Loading und Größenanpassung.

Frage 3: Wo platziert man die Middleware-Datei?

Antwort anzeigen Im Projekt-Root (`middleware.js`).

🚀 Weiter zu Kapitel 14: Deployment & Live-Schaltung

Frei für alle Anfänger