Blazor von Grund auf: Kapitel 3 — Komponenten, die skalieren

· 3 Min. Lesezeit

Willkommen zu Kapitel 3 von Blazor von Grund auf. Wenn du Kapitel 2 verpasst hast, lies es zuerst, damit dein Basisprojekt steht.

In Kapitel 2 haben wir die App zum Laufen gebracht. In diesem Kapitel machen wir sie wartbar.

Blazor-Projekte werden schnell unübersichtlich, wenn jede Seite eine riesige .razor-Datei ist. Komponenten verhindern das: mehr Konsistenz, mehr Wiederverwendung, klarere Grenzen zwischen UI-Teilen.


Was eine Blazor-Komponente wirklich ist

Eine Komponente ist eine .razor-Datei, die:

  • Markup rendert
  • Lokalen Zustand hält
  • Eingaben über Parameter annimmt
  • Events an Elternkomponenten meldet
  • Child Content rendert

Zur Laufzeit behandelt Blazor jede Komponente wie eine kleine Zustandsmaschine. Bei Zustandsänderungen wird neu gerendert und ein DOM-Diff angewendet.

Dein Ziel: klare Eingaben und vorhersagbares Verhalten.


Schritt 1: Mit einer fokussierten Komponente starten

Erstelle Components/Common/SectionHeader.razor:

<header class="section-header">
    <h2>@Title</h2>
    @if (!string.IsNullOrWhiteSpace(Subtitle))
    {
        <p>@Subtitle</p>
    }
</header>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public string? Subtitle { get; set; }
}

Verwendung in Components/Pages/Home.razor:

@page "/"

<PageTitle>Blazor von Grund auf</PageTitle>

<SectionHeader
    Title="Blazor von Grund auf"
    Subtitle="Kapitel 3 dreht sich um Komponenten." />

Klein, aber wichtig: Eine Komponente sollte allein über ihre Parameter verständlich sein.


Schritt 2: Verhalten über Parameter explizit machen

Wir bauen eine wiederverwendbare Button-Komponente.

Components/Common/AppButton.razor:

<button class="app-button @VariantCssClass" @onclick="OnClick">
    @Text
</button>

@code {
    [Parameter] public string Text { get; set; } = "Button";
    [Parameter] public string Variant { get; set; } = "primary";
    [Parameter] public EventCallback OnClick { get; set; }

    private string VariantCssClass => Variant.ToLowerInvariant() switch
    {
        "secondary" => "app-button--secondary",
        "danger" => "app-button--danger",
        _ => "app-button--primary"
    };
}

Nutzung:

@code {
    private int _savedCount;

    private void Save()
    {
        _savedCount++;
    }
}

<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>Gespeichert: @_savedCount Mal.</p>

Der Kern ist der Vertrag:

  • Wenige, klare Parameter
  • Explizite Namen statt magischem Verhalten
  • Sichere Standardwerte

Schritt 3: RenderFragment für Komposition nutzen

Mit RenderFragment kann eine Elternkomponente UI-Blöcke an ein Kind übergeben.

Erstelle Components/Common/Card.razor:

<article class="card">
    <header class="card__header">@Title</header>
    <section class="card__body">
        @ChildContent
    </section>
</article>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public RenderFragment? ChildContent { get; set; }
}

Nutzung:

<Card Title="Roadmap">
    <ul>
        <li>Komponenten</li>
        <li>Data Binding</li>
        <li>Routing</li>
    </ul>
</Card>

Damit baust du konsistente Seitenstrukturen ohne überall Wrapper-Markup zu kopieren.


Schritt 4: Komposition statt riesiger Seiten

Wenn eine Seite zu groß wird, nach Verantwortung aufteilen:

  • ProfileSummary für den Kopfbereich
  • ProfileStats für Kennzahlen
  • ProfileActivityList für letzte Aktivitäten

Die Seite wird so zur Orchestrierung, nicht zur Detail-Implementierung.


Schritt 5: Markup und Logik im Gleichgewicht halten

Für kleine Komponenten ist inline @code völlig okay.

Für größere Komponenten: Code-Behind nutzen:

  • UserCard.razor
  • UserCard.razor.cs

So bleiben Markup und C#-Logik jeweils besser lesbar.


Schritt 6: Eine pragmatische Ordnerstruktur

Eine Struktur, die gut skaliert:

  • Components/Pages/ -> routbare Seiten
  • Components/Layout/ -> App-Shell und Navigation
  • Components/Common/ -> geteilte generische Bausteine
  • Components/Features/<FeatureName>/ -> feature-spezifische Komponenten

Häufige Fehler am Anfang

  • Zu viele Parameter statt eines dedizierten Modells
  • Business-Logik direkt in Seitenkomponenten
  • Eine “God Component” mit hunderten Zeilen
  • Wiederholtes Markup statt kleiner wiederverwendbarer Komponenten

Ausblick

In Kapitel 4 geht es um Data Binding und Events: @bind, Event-Handling, Trade-offs von Two-Way-Binding und Muster für vorhersagbaren Zustand.