Blazor à partir de zéro : Chapitre 3 — Des composants qui passent à l’échelle

· 3 min de lecture

Bienvenue dans le Chapitre 3 de Blazor à partir de zéro. Si vous avez manqué le Chapitre 2, commencez par là pour avoir le projet de base prêt.

Au Chapitre 2, on a fait tourner l’application. Ici, on la rend maintenable.

Les apps Blazor deviennent vite désordonnées quand chaque page finit en gros fichier .razor. Les composants évitent ça : plus de cohérence, plus de réutilisation, et des frontières claires entre les morceaux d’UI.


Ce qu’est vraiment un composant Blazor

Un composant est un fichier .razor qui peut :

  • Rendre du markup
  • Gérer un état local
  • Recevoir des entrées via des paramètres
  • Remonter des événements au parent
  • Rendre du contenu enfant

À l’exécution, Blazor traite chaque composant comme une petite machine d’état. Quand l’état change, Blazor re-rend et applique un diff DOM.

Votre objectif : des entrées propres et un comportement prévisible.


Étape 1 : Commencer avec un composant ciblé

Créez 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; }
}

Utilisez-le dans Components/Pages/Home.razor :

@page "/"

<PageTitle>Blazor à partir de zéro</PageTitle>

<SectionHeader
    Title="Blazor à partir de zéro"
    Subtitle="Le chapitre 3 est consacré aux composants." />

Petit exemple, grande idée : un composant doit être lisible via ses paramètres.


Étape 2 : Utiliser les paramètres pour expliciter le comportement

Créons un bouton réutilisable.

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

Utilisation :

@code {
    private int _savedCount;

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

<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>Sauvegardé @_savedCount fois.</p>

Le point central est le contrat :

  • Paramètres simples et clairs
  • Noms explicites plutôt que logique implicite
  • Valeurs par défaut sûres

Étape 3 : Utiliser RenderFragment pour la composition

RenderFragment permet au parent d’injecter des blocs d’UI dans l’enfant.

Créez 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; }
}

Utilisation :

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

Ce pattern évite de répéter le même markup de structure partout.


Étape 4 : Préférer la composition aux pages géantes

Quand une page grossit trop, découpez par responsabilité :

  • ProfileSummary pour le bloc d’identité
  • ProfileStats pour les métriques
  • ProfileActivityList pour l’activité récente

La page devient orchestration, pas implémentation.


Étape 5 : Équilibrer markup et logique

Pour les composants simples, @code inline suffit.

Pour les composants plus gros, passez en code-behind :

  • UserCard.razor
  • UserCard.razor.cs

Vous gagnez en lisibilité côté markup et côté logique C#.


Étape 6 : Une structure de dossiers pragmatique

Une structure qui passe bien à l’échelle :

  • Components/Pages/ -> pages routables
  • Components/Layout/ -> shell applicatif et navigation
  • Components/Common/ -> blocs génériques partagés
  • Components/Features/<FeatureName>/ -> composants spécifiques à une feature

Erreurs fréquentes au début

  • Trop de paramètres au lieu d’un modèle dédié
  • Règles métier directement dans les pages
  • Un composant “Dieu” de centaines de lignes
  • Duplication de markup au lieu d’extraction de composants

Chapitre suivant

Dans le Chapitre 4, on abordera data binding et événements : @bind, gestion des événements, compromis du two-way binding et patterns pour garder un état prévisible.