Blazor od podstaw: Rozdział 3 — Komponenty, które skalują się z projektem

· 3 min czytania

Witamy w Rozdziale 3 serii Blazor od podstaw. Jeśli pominąłeś Rozdział 2, wróć do niego najpierw, żeby mieć gotowy projekt bazowy.

W Rozdziale 2 uruchomiliśmy aplikację. W tym rozdziale sprawimy, że będzie utrzymywalna.

Aplikacje Blazor szybko robią się chaotyczne, gdy każda strona staje się ogromnym plikiem .razor. Komponenty pomagają tego uniknąć: dają spójność, reużywalność i czytelne granice między fragmentami UI.


Czym naprawdę jest komponent Blazor

Komponent to plik .razor, który potrafi:

  • Renderować markup
  • Trzymać lokalny stan
  • Przyjmować dane przez parametry
  • Wysyłać zdarzenia do komponentu nadrzędnego
  • Renderować treść potomną

W runtime Blazor traktuje komponent jak małą maszynę stanów. Gdy stan się zmienia, framework renderuje ponownie i stosuje diff DOM.

Dlatego warto projektować jasne wejścia i przewidywalne zachowanie.


Krok 1: Zacznij od jednego, konkretnego komponentu

Utwórz 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; }
}

Użycie w Components/Pages/Home.razor:

@page "/"

<PageTitle>Blazor od podstaw</PageTitle>

<SectionHeader
    Title="Blazor od podstaw"
    Subtitle="Rozdział 3 jest o komponentach." />

To mały przykład, ale pokazuje kluczową zasadę: komponent powinien być czytelny przez same parametry.


Krok 2: Parametry jako jawny kontrakt

Stwórzmy reużywalny przycisk.

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"
    };
}

Użycie:

@code {
    private int _savedCount;

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

<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>Zapisano @_savedCount razy.</p>

Najważniejszy jest kontrakt:

  • Mało parametrów, ale konkretnych
  • Jawne nazwy zamiast ukrytej “magii”
  • Bezpieczne wartości domyślne

Krok 3: RenderFragment do kompozycji layoutu

RenderFragment pozwala rodzicowi przekazać blok UI do komponentu dziecka.

Utwórz 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; }
}

Użycie:

<Card Title="Roadmap">
    <ul>
        <li>Komponenty</li>
        <li>Data binding</li>
        <li>Routing</li>
    </ul>
</Card>

To wzorzec, który pozwala budować spójny layout bez duplikowania wrapperów.


Krok 4: Kompozycja zamiast gigantycznych stron

Jeśli strona rośnie za bardzo, rozdziel ją wg odpowiedzialności:

  • ProfileSummary dla części nagłówkowej
  • ProfileStats dla metryk
  • ProfileActivityList dla ostatniej aktywności

Wtedy strona staje się orkiestracją, a nie miejscem na wszystkie detale.


Krok 5: Równowaga między markupem a logiką

Dla prostych komponentów inline @code jest w porządku.

Dla większych komponentów przenieś logikę do code-behind:

  • UserCard.razor
  • UserCard.razor.cs

Dzięki temu zarówno markup, jak i logika C# są czytelniejsze.


Krok 6: Praktyczna struktura folderów

Struktura, która zwykle dobrze działa:

  • Components/Pages/ -> strony routowalne
  • Components/Layout/ -> shell aplikacji i nawigacja
  • Components/Common/ -> współdzielone, generyczne klocki
  • Components/Features/<FeatureName>/ -> komponenty specyficzne dla funkcji

Najczęstsze błędy na początku

  • Za dużo parametrów zamiast dedykowanego modelu widoku
  • Reguły biznesowe upchnięte w komponentach stron
  • Jeden “boski komponent” z setkami linii
  • Powielanie tych samych fragmentów markupu zamiast ekstrakcji komponentów

Co dalej

W Rozdziale 4 skupimy się na data binding i zdarzeniach: @bind, obsługa zdarzeń, kompromisy two-way binding i wzorce utrzymujące przewidywalny stan.