Blazor von Grund auf: Kapitel 3 — Komponenten, die skalieren
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:
ProfileSummaryfür den KopfbereichProfileStatsfür KennzahlenProfileActivityListfü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.razorUserCard.razor.cs
So bleiben Markup und C#-Logik jeweils besser lesbar.
Schritt 6: Eine pragmatische Ordnerstruktur
Eine Struktur, die gut skaliert:
Components/Pages/-> routbare SeitenComponents/Layout/-> App-Shell und NavigationComponents/Common/-> geteilte generische BausteineComponents/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.