Przejdź do treści
Frontend

Svelte und SvelteKit - Eine neue Ara der Frontend-Entwicklung

Veröffentlicht am:
·6 Min. Lesezeit·Autor: MDS Software Solutions Group

Svelte und SvelteKit

frontend

Svelte und SvelteKit - Eine neue Ara der Frontend-Entwicklung

Die Frontend-Welt steht niemals still. Nach Jahren der Dominanz von React und Vue betritt Svelte die Buehne - ein Framework, das die Art und Weise, wie wir ueber den Aufbau von Benutzeroberflaechen denken, grundlegend veraendert. Anstatt wie traditionelle Frameworks im Browser zu laufen, verlagert Svelte die schwere Arbeit auf den Kompilierungsschritt und erzeugt optimierten, imperativen JavaScript-Code. In Kombination mit SvelteKit - einem vollwertigen Meta-Framework fuer den Aufbau von Applikationen - erhaelt man ein Werkzeug, das aussergewoehnliche Performance mit beispielloser Code-Einfachheit vereint.

Ein Compiler statt Virtual DOM#

Traditionelle Frameworks wie React und Vue setzen auf ein Virtual DOM - eine abstrakte, im Speicher gehaltene Darstellung des DOM-Baums. Bei jeder Zustandsaenderung vergleicht das Framework das neue Virtual DOM mit dem alten (Diffing) und wendet dann die minimalen Aenderungen auf das echte DOM an. Obwohl dieser Ansatz gut funktioniert, erzeugt er Laufzeit-Overhead und erfordert, dass der Framework-Code an den Browser ausgeliefert wird.

Svelte verwirft dieses Paradigma vollstaendig. Anstatt Komponenten zur Laufzeit zu interpretieren, ist Svelte ein Compiler, der deklarativen Komponentencode waehrend des Build-Prozesses in optimiertes JavaScript umwandelt. Der generierte Code manipuliert das DOM direkt, ohne jegliche Zwischenschicht.

<!-- Svelte kompiliert dies zu nativen DOM-Operationen -->
<script>
  let count = $state(0);

  function increment() {
    count++;
  }
</script>

<button onclick={increment}>
  {count} Mal geklickt
</button>

Die obige Komponente wird zu Code kompiliert, der direkt element.textContent = ... und element.addEventListener(...) aufruft. Es gibt kein Virtual DOM, kein Diffing - nur praezise, chirurgische DOM-Updates.

Vorteile des Compiler-Ansatzes#

  • Kleinere Bundles - der Browser muss keinen Framework-Code herunterladen (keine Runtime)
  • Schnelleres Rendering - kein Overhead durch Virtual DOM und Reconciliation
  • Geringerer Speicherverbrauch - kein Virtual-DOM-Baum muss im Speicher gehalten werden
  • Bessere Performance auf schwachen Geraeten - weniger Berechnungen zur Laufzeit

Svelte 5 Reaktivitaetssystem - Runes#

Svelte 5 fuehrte ein revolutionaeres Reaktivitaetssystem ein, das auf Runes basiert - speziellen Compiler-Signalen, die mit dem $-Zeichen beginnen. Runes ersetzten das fruehere, auf Zuweisungen basierende Reaktivitaetssystem und bieten groessere Praezision, bessere Komposition und volle TypeScript-Kompatibilitaet.

$state - Reaktiver Zustand#

Die $state-Rune deklariert eine reaktive Zustandsvariable. Jede Aenderung dieser Variable aktualisiert automatisch alle Stellen im DOM, die von ihr abhaengen.

<script>
  let name = $state('Svelte');
  let items = $state([1, 2, 3]);

  // Objekte und Arrays sind tief reaktiv
  function addItem() {
    items.push(items.length + 1); // automatisch erkannt!
  }
</script>

<h1>Hallo, {name}!</h1>
<p>Elemente: {items.join(', ')}</p>
<button onclick={addItem}>Element hinzufuegen</button>

$derived - Abgeleitete Werte#

Die $derived-Rune erstellt einen Wert, der aus anderen reaktiven Zustaenden berechnet wird. Svelte verfolgt automatisch die Abhaengigkeiten und berechnet den Wert nur dann neu, wenn sich diese aendern.

<script>
  let width = $state(10);
  let height = $state(20);

  // Automatisch neu berechnet, wenn sich width oder height aendert
  let area = $derived(width * height);

  // Fuer komplexere Berechnungen
  let description = $derived.by(() => {
    if (area > 100) return 'Grosses Rechteck';
    if (area > 50) return 'Mittleres Rechteck';
    return 'Kleines Rechteck';
  });
</script>

<p>Flaeche: {area} ({description})</p>

$effect - Seiteneffekte#

Die $effect-Rune ermoeglicht es, auf Zustandsaenderungen zu reagieren und Seiteneffekte auszufuehren, wie z.B. Logging, Synchronisation mit externen APIs oder DOM-Manipulation.

<script>
  let query = $state('');
  let results = $state([]);

  $effect(() => {
    // Verfolgt automatisch die Abhaengigkeit von `query`
    if (query.length < 3) {
      results = [];
      return;
    }

    const controller = new AbortController();

    fetch(`/api/search?q=${query}`, { signal: controller.signal })
      .then(r => r.json())
      .then(data => { results = data; });

    // Aufraeum-Funktion
    return () => controller.abort();
  });
</script>

<input bind:value={query} placeholder="Suchen..." />
{#each results as result}
  <p>{result.title}</p>
{/each}

$props - Komponenten-Eigenschaften#

Die $props-Rune definiert die Eigenschaften, die eine Komponente akzeptiert, mit voller Unterstuetzung fuer Destrukturierung und Standardwerte.

<!-- Button.svelte -->
<script>
  let {
    variant = 'primary',
    size = 'md',
    disabled = false,
    children,
    onclick,
    ...rest
  } = $props();
</script>

<button
  class="btn btn-{variant} btn-{size}"
  {disabled}
  {onclick}
  {...rest}
>
  {@render children()}
</button>

Anatomie einer Svelte-Komponente#

Svelte-Komponenten sind .svelte-Dateien, die drei Abschnitte enthalten: Logik (<script>), HTML-Template und Styles (<style>). Styles sind standardmaessig scoped - sie gelten nur fuer die jeweilige Komponente.

<script lang="ts">
  import type { User } from '$lib/types';

  let { user }: { user: User } = $props();
  let isExpanded = $state(false);

  let initials = $derived(
    user.firstName[0] + user.lastName[0]
  );
</script>

<div class="user-card" class:expanded={isExpanded}>
  <div class="avatar">{initials}</div>
  <div class="info">
    <h3>{user.firstName} {user.lastName}</h3>
    <p>{user.email}</p>
  </div>
  <button onclick={() => isExpanded = !isExpanded}>
    {isExpanded ? 'Einklappen' : 'Ausklappen'}
  </button>

  {#if isExpanded}
    <div class="details">
      <p>Beigetreten: {user.joinDate.toLocaleDateString('de-DE')}</p>
      <p>Rolle: {user.role}</p>
    </div>
  {/if}
</div>

<style>
  .user-card {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    padding: 1rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    transition: box-shadow 0.2s;
  }

  .user-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }

  .avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: #6366f1;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
  }

  .expanded {
    border-color: #6366f1;
  }
</style>

Stores - Globaler Anwendungszustand#

Svelte bietet ein eingebautes System zur Verwaltung des globalen Zustands durch Stores. Stores sind reaktive Datencontainer, die komponentenuebergreifend geteilt werden koennen.

// src/lib/stores/cart.ts
import { writable, derived } from 'svelte/store';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

export const cartItems = writable<CartItem[]>([]);

export const cartTotal = derived(cartItems, ($items) =>
  $items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);

export const cartCount = derived(cartItems, ($items) =>
  $items.reduce((sum, item) => sum + item.quantity, 0)
);

export function addToCart(product: { id: string; name: string; price: number }) {
  cartItems.update(items => {
    const existing = items.find(i => i.id === product.id);
    if (existing) {
      existing.quantity++;
      return [...items];
    }
    return [...items, { ...product, quantity: 1 }];
  });
}

export function removeFromCart(id: string) {
  cartItems.update(items => items.filter(i => i.id !== id));
}

Verwendung in einer Komponente mit der Auto-Subscribe-Syntax ($):

<script>
  import { cartItems, cartTotal, cartCount, removeFromCart } from '$lib/stores/cart';
</script>

<h2>Warenkorb ({$cartCount} Artikel)</h2>

{#each $cartItems as item (item.id)}
  <div class="cart-item">
    <span>{item.name} x{item.quantity}</span>
    <span>{(item.price * item.quantity).toFixed(2)} EUR</span>
    <button onclick={() => removeFromCart(item.id)}>Entfernen</button>
  </div>
{/each}

<p class="total">Gesamt: {$cartTotal.toFixed(2)} EUR</p>

SvelteKit - Das vollstaendige Meta-Framework#

SvelteKit ist das offizielle Meta-Framework fuer Svelte, vergleichbar mit Next.js fuer React oder Nuxt fuer Vue. Es bietet alles, was Sie fuer den Aufbau moderner Webanwendungen benoetigen: dateibasiertes Routing, Server-Side Rendering, Static Site Generation und vieles mehr.

Dateibasiertes Routing#

SvelteKit verwendet ein dateibasiertes Routing-System, das durch die Verzeichnisstruktur im Ordner src/routes gesteuert wird. Jedes Verzeichnis mit einer +page.svelte-Datei erstellt eine neue Route.

src/routes/
├── +page.svelte          → /
├── +layout.svelte        → Root-Layout fuer die gesamte App
├── about/
│   └── +page.svelte      → /about
├── blog/
│   ├── +page.svelte      → /blog
│   ├── +page.server.ts   → Load-Funktion fuer /blog
│   └── [slug]/
│       ├── +page.svelte  → /blog/:slug
│       └── +page.server.ts
├── api/
│   └── products/
│       └── +server.ts    → API-Endpunkt: /api/products
└── (auth)/
    ├── +layout.svelte    → Gruppen-Layout (beeinflusst die URL nicht)
    ├── login/
    │   └── +page.svelte  → /login
    └── register/
        └── +page.svelte  → /register

Layouts - Gemeinsame Vorlagen#

Layouts in SvelteKit ermoeglichen es, eine gemeinsame Seitenstruktur (Header, Navigation, Footer) zu definieren, die von untergeordneten Routen geteilt wird.

<!-- src/routes/+layout.svelte -->
<script>
  import Header from '$lib/components/Header.svelte';
  import Footer from '$lib/components/Footer.svelte';
  let { children } = $props();
</script>

<div class="app">
  <Header />
  <main>
    {@render children()}
  </main>
  <Footer />
</div>

<style>
  .app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }
  main {
    flex: 1;
    padding: 2rem;
    max-width: 1200px;
    margin: 0 auto;
  }
</style>

Load-Funktionen - Daten laden#

Load-Funktionen bilden das Herzstuck des Datenlade-Mechanismus von SvelteKit. Sie koennen auf dem Server (+page.server.ts) oder universell (+page.ts) ausgefuehrt werden.

// src/routes/blog/+page.server.ts
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/database';

export const load: PageServerLoad = async ({ url, locals }) => {
  const page = Number(url.searchParams.get('page')) || 1;
  const limit = 10;

  const posts = await db.post.findMany({
    where: { published: true },
    orderBy: { createdAt: 'desc' },
    skip: (page - 1) * limit,
    take: limit,
    select: {
      slug: true,
      title: true,
      excerpt: true,
      createdAt: true,
      author: { select: { name: true } }
    }
  });

  const total = await db.post.count({ where: { published: true } });

  return {
    posts,
    pagination: {
      page,
      totalPages: Math.ceil(total / limit),
      total
    }
  };
};

Die von load zurueckgegebenen Daten sind automatisch in der Seitenkomponente verfuegbar:

<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';
  import PostCard from '$lib/components/PostCard.svelte';

  let { data }: { data: PageData } = $props();
</script>

<h1>Blog</h1>

<div class="posts-grid">
  {#each data.posts as post (post.slug)}
    <PostCard {post} />
  {/each}
</div>

{#if data.pagination.totalPages > 1}
  <nav class="pagination">
    {#each Array(data.pagination.totalPages) as _, i}
      <a
        href="/blog?page={i + 1}"
        class:active={data.pagination.page === i + 1}
      >
        {i + 1}
      </a>
    {/each}
  </nav>
{/if}

Rendering-Strategien: SSR, SSG und CSR#

SvelteKit bietet eine flexible Auswahl der Rendering-Strategie auf Ebene einzelner Routen.

// SSR (Standard) - Server-Side Rendering bei jeder Anfrage
export const ssr = true;

// SSG - Statische Generierung zur Build-Zeit
export const prerender = true;

// CSR - Ausschliesslich Client-Side Rendering
export const ssr = false;
export const csr = true;

// Hybrid - Statische Seite mit Client-seitiger Hydration
export const prerender = true;
export const csr = true;

Form Actions - Formularverarbeitung#

Form Actions sind SvelteKits eleganter Mechanismus zur serverseitigen Formularverarbeitung, der auch ohne JavaScript funktioniert (Progressive Enhancement).

// src/routes/contact/+page.server.ts
import type { Actions } from './$types';
import { fail } from '@sveltejs/kit';
import { z } from 'zod';

const contactSchema = z.object({
  name: z.string().min(2, 'Der Name muss mindestens 2 Zeichen lang sein'),
  email: z.string().email('Ungueltige E-Mail-Adresse'),
  message: z.string().min(10, 'Die Nachricht muss mindestens 10 Zeichen lang sein')
});

export const actions: Actions = {
  default: async ({ request }) => {
    const formData = await request.formData();

    const data = {
      name: formData.get('name') as string,
      email: formData.get('email') as string,
      message: formData.get('message') as string
    };

    const result = contactSchema.safeParse(data);

    if (!result.success) {
      return fail(400, {
        data,
        errors: result.error.flatten().fieldErrors
      });
    }

    await sendEmail(result.data);

    return { success: true };
  }
};
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
  import { enhance } from '$app/forms';
  import type { ActionData } from './$types';

  let { form }: { form: ActionData } = $props();
</script>

<form method="POST" use:enhance>
  <label>
    Name
    <input name="name" value={form?.data?.name ?? ''} />
    {#if form?.errors?.name}
      <span class="error">{form.errors.name[0]}</span>
    {/if}
  </label>

  <label>
    E-Mail
    <input name="email" type="email" value={form?.data?.email ?? ''} />
    {#if form?.errors?.email}
      <span class="error">{form.errors.email[0]}</span>
    {/if}
  </label>

  <label>
    Nachricht
    <textarea name="message">{form?.data?.message ?? ''}</textarea>
    {#if form?.errors?.message}
      <span class="error">{form.errors.message[0]}</span>
    {/if}
  </label>

  <button type="submit">Senden</button>

  {#if form?.success}
    <p class="success">Ihre Nachricht wurde gesendet!</p>
  {/if}
</form>

API-Routen#

SvelteKit ermoeglicht das Erstellen von API-Endpunkten mit +server.ts-Dateien:

// src/routes/api/products/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { db } from '$lib/server/database';

export const GET: RequestHandler = async ({ url }) => {
  const category = url.searchParams.get('category');
  const page = Number(url.searchParams.get('page')) || 1;

  const products = await db.product.findMany({
    where: category ? { category } : undefined,
    skip: (page - 1) * 20,
    take: 20
  });

  return json(products);
};

export const POST: RequestHandler = async ({ request, locals }) => {
  if (!locals.user?.isAdmin) {
    throw error(403, 'Zugriff verweigert');
  }

  const body = await request.json();
  const product = await db.product.create({ data: body });

  return json(product, { status: 201 });
};

Uebergaenge und Animationen#

Eines der herausragendsten Merkmale von Svelte ist das eingebaute Uebergangs- und Animationssystem. Anstatt auf Drittanbieter-Bibliotheken zurueckzugreifen, koennen Sie Elemente elegant mit Direktiven animieren.

<script>
  import { fade, fly, slide, scale } from 'svelte/transition';
  import { flip } from 'svelte/animate';
  import { quintOut } from 'svelte/easing';

  let items = $state([
    { id: 1, text: 'Erstes' },
    { id: 2, text: 'Zweites' },
    { id: 3, text: 'Drittes' }
  ]);

  let showPanel = $state(false);

  function addItem() {
    items = [...items, {
      id: Date.now(),
      text: `Element ${items.length + 1}`
    }];
  }

  function removeItem(id: number) {
    items = items.filter(i => i.id !== id);
  }
</script>

<button onclick={() => showPanel = !showPanel}>
  Panel umschalten
</button>

{#if showPanel}
  <div transition:fly={{ y: -20, duration: 300 }}>
    <p>Panel mit Fly-Animation</p>
  </div>
{/if}

<button onclick={addItem}>Element hinzufuegen</button>

<ul>
  {#each items as item (item.id)}
    <li
      animate:flip={{ duration: 300 }}
      in:scale={{ duration: 200, easing: quintOut }}
      out:fade={{ duration: 150 }}
    >
      {item.text}
      <button onclick={() => removeItem(item.id)}>x</button>
    </li>
  {/each}
</ul>

TypeScript-Unterstuetzung#

Svelte und SvelteKit bieten hervorragende TypeScript-Unterstuetzung. Die von SvelteKit automatisch generierten Typen gewaehrleisten Typsicherheit fuer Load-Funktionen, Form Actions und Routenparameter.

<script lang="ts">
  import type { PageData } from './$types';

  interface Product {
    id: string;
    name: string;
    price: number;
    inStock: boolean;
  }

  let { data }: { data: PageData } = $props();

  let searchQuery = $state('');
  let sortBy = $state<'name' | 'price'>('name');

  let filteredProducts = $derived(
    data.products
      .filter((p: Product) =>
        p.name.toLowerCase().includes(searchQuery.toLowerCase())
      )
      .sort((a: Product, b: Product) =>
        sortBy === 'price' ? a.price - b.price : a.name.localeCompare(b.name)
      )
  );
</script>

Svelte vs React vs Vue - Vergleich#

Bundle-Groesse#

| Framework | Minimales Bundle (gzip) | |-----------|------------------------| | Svelte 5 | ~2 KB | | Vue 3 | ~33 KB | | React 18 | ~42 KB |

Svelte erzeugt deutlich kleinere Bundles, da der Framework-Code einkompiliert wird - der Browser laedt nur herunter, was tatsaechlich verwendet wird.

Developer Experience#

| Merkmal | Svelte | React | Vue | |------------------------|-----------------|------------------|------------------| | Lernkurve | Sanft | Mittel | Sanft | | Boilerplate | Minimal | Erheblich (Hooks)| Moderat | | Eingebaute Animationen | Ja | Nein | Ja (begrenzt) | | Scoped CSS | Standardmaessig | CSS Modules/CSS-in-JS | Scoped-Attribut | | Zwei-Wege-Bindung | bind:value | Manuell | v-model | | Zustandsverwaltung | Runes + Stores | useState/Redux | ref/Pinia |

Laufzeit-Performance#

Svelte belegt in Performance-Benchmarks fuer Frontend-Frameworks durchgehend Spitzenpositionen. Das Fehlen des Virtual-DOM-Overheads und die Kompilierung zu imperativem Code fuehren zu:

  • Schnelleren DOM-Updates
  • Geringerem Speicherverbrauch
  • Besserer Reaktionsfaehigkeit auf leistungsschwachen Geraeten
  • Schnellerer Time to Interactive (TTI)

Wann sollte man Svelte und SvelteKit waehlen?#

Svelte ist eine ausgezeichnete Wahl in folgenden Szenarien:

  • Anwendungen mit hoechsten Performance-Anforderungen - Online-Shops, Dashboards, Echtzeit-Apps
  • Projekte mit begrenztem Datentransfer-Budget - Mobile Apps, aufstrebende Maerkte
  • Animationsintensive Websites - das eingebaute Uebergangssystem eliminiert den Bedarf an Drittanbieter-Bibliotheken
  • MVPs und Prototypen - minimaler Boilerplate beschleunigt die Entwicklung
  • Statische Projekte mit interaktiven Elementen - SvelteKits SSG kombiniert mit Inseln der Interaktivitaet
  • Full-Stack-Anwendungen - SvelteKit mit Form Actions und API-Routen eliminiert den Bedarf an einem separaten Backend

Wann Alternativen in Betracht ziehen?#

  • Ein umfangreiches UI-Komponenten-Oekosystem ist entscheidend - React bietet die groesste Auswahl an fertigen Komponenten
  • Ihr Team hat umfangreiche React/Vue-Erfahrung - Umschulungskosten koennen ein Faktor sein
  • React Native-Integration ist erforderlich - Svelte hat kein Aequivalent fuer native mobile Apps

Fazit#

Svelte und SvelteKit repraesentieren eine neue Philosophie fuer den Aufbau von Frontend-Anwendungen. Der Compiler-Ansatz, das intuitive Runes-basierte Reaktivitaetssystem, eingebaute Animationen und SvelteKits elegantes Routing bilden ein Oekosystem, das gleichzeitig leistungsstark und einfach zu bedienen ist.

Die wachsende Popularitaet von Svelte, das sich erweiternde Komponenten- und Tooling-Oekosystem sowie die Unterstuetzung durch Vercel (das Sveltes Schoepfer Rich Harris eingestellt hat) zeigen, dass Svelte kein voruebergehender Trend ist, sondern eine Technologie, die sich einen festen Platz in der modernen Frontend-Landschaft gesichert hat.

Brauchen Sie eine moderne Frontend-Anwendung?#

Bei MDS Software Solutions Group entwickeln wir leistungsstarke Webanwendungen mit modernsten Technologien, darunter Svelte und SvelteKit. Wir bieten:

  • Entwicklung von Webanwendungen von Grund auf mit Svelte/SvelteKit
  • Migration bestehender Projekte von React oder Vue zu Svelte
  • Performance-Optimierung bestehender Anwendungen
  • Technologieberatung und Auswahl des besten Tech-Stacks
  • Support und Wartung von Produktionsanwendungen

Kontaktieren Sie uns, um Ihr Projekt zu besprechen und zu entdecken, wie Svelte die Entwicklung Ihrer Anwendung beschleunigen kann!

Autor
MDS Software Solutions Group

Team von Programmierexperten, die sich auf moderne Webtechnologien spezialisiert haben.

Svelte und SvelteKit - Eine neue Ara der Frontend-Entwicklung | MDS Software Solutions Group | MDS Software Solutions Group