Développement Web

Blazor en .NET 10 : modes de rendu unifiés, PersistentState et passkeys

8 دقائق للقراءة

Blazor en .NET 10 : un modèle unifié, des composants partout

Blazor a longtemps été perçu comme une collection de variantes mutuellement exclusives : Blazor Server pour le rendu côté serveur via SignalR, Blazor WebAssembly pour exécuter du C# dans le navigateur, et plus récemment Blazor SSR (server-side rendering) pour des pages statiques. .NET 8 a posé les bases du modèle unifié appelé Blazor Web App, et .NET 10 finalise la convergence : un même composant Razor peut s’exécuter dans n’importe lequel des quatre modes (Static, Server, WebAssembly, Auto) avec un attribut @rendermode. Les release notes officielles d’ASP.NET Core 10 confirment plusieurs ajouts clés en 2026 : [PersistentState] déclaratif, persistance de circuit pour résister aux déconnexions, Hot Reload activé par défaut pour WebAssembly, et nouveau composant NotFoundPage.

Cette série pratique vous emmène depuis le template Blazor Web App jusqu’à une application complète mêlant pages statiques rapides à indexer, listes interactives Server, formulaires WASM, et authentification passkey. Vous comprendrez pourquoi le mode Auto est devenu la cible privilégiée pour la plupart des projets, et comment éviter les pièges du prerendering.

Étape 1 : Générer un projet Blazor Web App .NET 10

Dans un dossier vide :

dotnet new blazor -n BlazorBoutique --interactivity Auto --use-program-main
cd BlazorBoutique
dotnet run

L’option --interactivity Auto active les deux modes Server et WebAssembly dans le même projet, avec sélection automatique selon le contexte. Le template génère deux assemblies : BlazorBoutique (côté serveur) et BlazorBoutique.Client (côté WASM). Ouvrez le navigateur sur l’URL affichée. La page d’accueil s’affiche en SSR pur, le compteur passe en Server au premier clic, et le formulaire météo bascule en WebAssembly une fois le runtime téléchargé en arrière-plan. Signal de réussite : la console réseau du navigateur montre un seul aller-retour HTTP pour la page d’accueil, puis un WebSocket SignalR ouvert au premier clic interactif.

Étape 2 : Comprendre les quatre modes de rendu

En .NET 10, chaque composant Razor déclare son comportement via l’attribut @rendermode :

  • Static (par défaut) : rendu HTML pur côté serveur, aucune interactivité, idéal pour pages indexables et contenu statique.
  • Server (@rendermode InteractiveServer) : code C# exécuté sur le serveur, état maintenu via SignalR. Faible bande passante consommée mais latence dépendante du réseau.
  • WebAssembly (@rendermode InteractiveWebAssembly) : code C# téléchargé puis exécuté dans le navigateur. Cold start de 1-3 secondes, mais interactivité quasi-instantanée ensuite.
  • Auto (@rendermode InteractiveAuto) : démarre en Server pour interactivité immédiate, puis bascule en WebAssembly une fois les ressources téléchargées en arrière-plan. Combine les deux mondes.

Pour appliquer un mode à une page entière, ajoutez en tête du fichier .razor :

@page "/produits"
@rendermode InteractiveAuto

Pour un seul composant enfant dans une page Static, posez l’attribut sur l’instance :

<Compteur @rendermode="InteractiveServer" />

Signal de réussite : les pages Static apparaissent dans le rapport view-source avec leur contenu complet HTML, tandis que les composants Server affichent un placeholder remplacé après connexion.

Étape 3 : Persister l’état avec [PersistentState]

Avant .NET 10, persister un état entre prerendering et hydratation interactive exigeait du code boilerplate avec PersistentComponentState. .NET 10 introduit l’attribut [PersistentState] qui rend cette opération déclarative :

@page "/catalogue"
@rendermode InteractiveAuto
@inject CatalogueService Service

@if (Produits is null)
{
    <p>Chargement…</p>
}
else
{
    foreach (var p in Produits)
    {
        <div>@p.Nom – @p.Prix € </div>
    }
}

@code {
    [PersistentState]
    private List<Produit>? Produits { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Produits ??= await Service.GetProduitsAsync();
    }
}

Lors du prerendering côté serveur, la liste est chargée et sérialisée dans la page HTML envoyée au navigateur. Lorsque le mode WebAssembly prend le relais, OnInitializedAsync détecte que Produits n’est pas null grâce à [PersistentState] et évite un second appel réseau. Signal de réussite : la console réseau montre un seul appel /api/produits en mode Auto.

Étape 4 : Résister aux déconnexions avec la persistance de circuit

Un des reproches historiques à Blazor Server est la perte d’état lorsque l’utilisateur passe en mode veille ou change brièvement de réseau. .NET 10 ajoute la persistance de circuit : l’état est sérialisé périodiquement côté serveur et restauré à la reconnexion. Activez le mécanisme dans Program.cs :

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents(options =>
    {
        options.DisconnectedCircuitMaxRetained = 100;
        options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(5);
    });

Sur une déconnexion réseau, le navigateur affiche désormais le composant ReconnectModal remanié en .NET 10 pour respecter une Content-Security-Policy stricte. Lorsque la connexion revient avant l’expiration, l’utilisateur retrouve son écran exact, panier rempli, champs partiellement remplis. Signal de réussite : couper le Wi-Fi du téléphone pendant 30 secondes, le réactiver, et constater que l’état du composant n’a pas été réinitialisé.

Étape 5 : Hot Reload natif en WebAssembly

En .NET 10, Hot Reload est activé par défaut en configuration Debug pour les composants WebAssembly. Lancez dotnet watch run et modifiez un fichier .razor côté client : la page met à jour ses composants sans rechargement complet ni rebuild WASM. Le gain de productivité est considérable, en particulier pour ajuster du CSS Tailwind ou des libellés en pleine session de développement. La feature détecte automatiquement les changements incompatibles (signature de méthode, ajout de champ static) et propose un redémarrage propre. Signal de réussite : modifier un texte dans la balise <h1> d’un composant client, sauvegarder, et voir le navigateur refléter le changement en moins de 500 ms sans perdre l’état des champs de formulaire.

Étape 6 : Ajouter l’authentification par passkey

ASP.NET Core 10 intègre le support natif des passkeys (WebAuthn) dans ASP.NET Core Identity, et les templates Blazor Web App incluent cette gestion par défaut. Régénérez un projet avec authentification :

dotnet new blazor -n BlazorSecure --interactivity Auto --auth Individual

Le projet inclut une page /Account/Manage/Passkeys qui permet à l’utilisateur d’enregistrer une clé de sécurité matérielle (YubiKey, Touch ID, Windows Hello) ou logicielle. Lors de la connexion, l’option « Se connecter avec une passkey » devient disponible automatiquement après le premier enregistrement. Le mécanisme est résistant au phishing : aucune saisie de mot de passe n’a lieu, le navigateur signe un challenge cryptographique vérifié côté serveur. Signal de réussite : se connecter sans mot de passe sur Chrome avec Windows Hello configuré, en ayant préalablement enregistré la passkey dans le panneau utilisateur.

Étape 7 : Gérer les pages introuvables avec NotFoundPage

Avant .NET 10, configurer une page 404 personnalisée dans Blazor exigeait de manipuler le composant Router à la main. .NET 10 introduit le paramètre NotFoundPage :

<Router AppAssembly="@typeof(Program).Assembly"
        NotFoundPage="@typeof(Pages.NotFoundPage)" />

Le composant NotFoundPage.razor reçoit automatiquement le contexte (URL demandée, méthode HTTP) et peut afficher un message contextualisé : suggestion de pages similaires, recherche, retour à l’accueil. Cette amélioration paraît mineure mais évite la quinzaine de lignes répétitives qu’on retrouvait dans tous les projets Blazor. Signal de réussite : naviguer vers /cette-page-nexiste-pas affiche votre design 404 personnalisé sans message générique.

Étape 8 : Améliorations JS Interop pour interagir avec des libs JavaScript

.NET 10 enrichit l’API JSRuntime pour créer des objets JavaScript et lire/modifier leurs propriétés directement depuis C#, sans passer par une fonction wrapper. Exemple :

// Côté JavaScript dans wwwroot/app.js
window.jsInterop = {
    TestClass: class { constructor(msg) { this.message = msg; } }
};

// Côté C# dans un composant
@inject IJSRuntime JS

@code {
    private async Task DemoAsync()
    {
        var instance = await JS.InvokeConstructorAsync("jsInterop.TestClass", "Salut Blazor !");
        await JS.SetValueAsync("jsInterop.testObject.num", 42);
        var val = await JS.GetValueAsync<int>("jsInterop.testObject.num");
        Console.WriteLine(val); // affiche 42
    }
}

Ces nouvelles primitives InvokeConstructorAsync, GetValueAsync et SetValueAsync évitent des helpers JavaScript globaux et facilitent l’intégration de bibliothèques tierces comme Leaflet, Chart.js ou Mapbox. Signal de réussite : la console du navigateur n’affiche aucune erreur et la valeur 42 est lue correctement depuis le C#.

Étape 9 : Validation de formulaires avec source generators

La validation des formulaires Blazor en .NET 10 utilise des source generators pour produire le code de validation à la compilation, ce qui élimine la reflection à l’exécution et rend le tout compatible Native AOT. Annotez votre modèle :

[ValidatableType]
public class Commande
{
    [Required, StringLength(60)]
    public string Client { get; set; } = "";

    public AdresseLivraison Adresse { get; set; } = new();

    public List<LigneCommande> Lignes { get; set; } = new();
}

L’attribut [ValidatableType] placé sur le type racine déclenche la génération du code de validation pour Commande, AdresseLivraison et LigneCommande de manière récursive. Plus besoin de wrapper FluentValidation ou de cascader manuellement les validations. Signal de réussite : un EditForm bindé sur une Commande invalide affiche les erreurs des objets et listes imbriqués sans le moindre code supplémentaire dans le composant.

Choisir le bon mode : guide pratique

Pour une landing page commerciale ou un blog : Static suffit, indexation parfaite par Google. Pour un dashboard administrateur avec tables et formulaires : Server fournit l’interactivité immédiate avec un coût serveur acceptable. Pour une application offline-first ou un jeu : WebAssembly est obligatoire. Pour la majorité des applications grand public : Auto offre le meilleur compromis — démarrage rapide en Server, charge serveur réduite une fois le WASM téléchargé. .NET 10 rend ce choix par composant, pas par projet entier. Vous pouvez avoir une page produit Static, un composant panier Server, et un éditeur de profil WebAssembly dans le même projet, sans la moindre acrobatie.

Service ITSkillsCenter

Site ou application web sur mesure

Conception Pro + Nom de domaine 1 an + Hébergement 1 an + Formation + Support 6 mois. Accès et code livrés. À partir de 350 000 FCFA.

Demander un devis
Publicité