Loupe

Blazor - partie 5 : Comment afficher une SPA Blazor webassembly plus rapidement grâce au prérendu serveur

Maintenant que nous avons obtenu une application qui fonctionne offline, voyons voir un peu comment afficher notre application Blazor webassembly plus rapidement au démarrage.

En effet, lorsque l'application est composée uniquement de fichiers statiques, tous les fichiers ont besoin d'être chargés avant de pouvoir afficher le rendu graphique, ce qui affiche un message "Loading..." à nos utilisateurs lors du premier accès à l’application.

Par contre, une fois les fichiers chargés, notre SPA reste bien entendu super rapide car elle n'a plus besoin de régénérer l'intégralité des pages sur notre serveur.

Maintenant, si vous vous souvenez de ce "bon vieux temps" où l'on faisait de l'aspnet MVC (Comment ça ce temps n'est pas si vieux que ça ? :)), recharger l'intégralité de la page lors de chaque action est bien entendu une surcharge qui ne nous manque pas le moins du monde. Mais au moins, le premier affichage de notre application web n'avait pas besoin de temps de chargement !

Si seulement nous pouvions avoir un système intelligent, avec un premier rendu effectué une première fois depuis le serveur, mais qui récupérerait ensuite tous les fichiers pour fonctionner en mode SPA en tâche de fond... Et bien ce temps-là est arrivé ! Réjouissons nous d'une fonctionnalité super basique mais pourtant si complexe pour notre SPA : Le Server Prerendering ! (Prérendu serveur en français)

En fait, le fonctionnement de Blazor en mode Server side Blazor (qui est d'ailleurs le seul mode de fonctionnement supporté en production à l'heure actuelle), utilise déjà ce rendu de composants Blazor côté serveur, puis utilise ensuite SignalR pour envoyer les modifications du DOM au navigateur web.

Mais voyons plus en détail comment ajouter ce prérendu serveur à notre SPA Blazor !

Est-ce que le prérendu serveur n'a que des avantages ?

Et bien pas vraiment. Sans même avoir besoin de parler des problématiques et limitations techniques engendrées par l'ajout du prérendu serveur (Du genre "double déclenchement de l’événement OnInitialized, etc. plus d'infos sur la partie Blazor Server de la doc officielle), l'un des points qui personnellement me marque le plus, c'est d'avoir à héberger notre application dans une application aspnetcore.

Même si en tant que développeur .NET, ça n'est pas vraiment un problème (voire ça pourrait même être un avantage), cela a quand même pour effet de réduire les possibilités d'hébergement de notre application (Le stockage dans de l'Azure Blob Storage n'est plus possible par exemple), et l'on aura besoin d'embarquer le runtime aspnetcore avec notre application pour qu'elle fonctionne (point qui est heureusement facilité avec les technologies de conteneurs telles que Docker)

Prérendu serveur d'une application Blazor webassembly avec aspnetcore

C'est parti, il est temps de rentrer un peu plus dans la technique ! Tout d'abord, nous aurons besoin d'une application aspnetcore 3, à créer avec votre IDE favori, ou via la ligne de commande dotnet new web.

On aura besoin de 2 choses :

  • Ajouter une référence vers le package Nuget Microsoft.AspNetCore.Blazor.Server,
  • et ajouter une référence vers notre projet Blazor.

Ensuite, on ajoute un fichier _Host.cshtml dans un dossier Pages afin de faire le rendu de notre composant Blazor racine App directement depuis le serveur, un peu "à la manière" de Razor pour faire le rendu de pages web. Le contenu de ce fichier devrait être identique à notre fichier index.html, modulo le remplacement de la balise affichant le message "Loading..." par la ligne suivante :

@using blazor_workbox_pwa
...
<app>@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))</app>

Le contenu intégral de ce fichier est disponible sur GitHub

Ensuite dans le fichier Startup.cs de notre application aspnetcore, on a besoin d'ajouter dans la méthode ConfigureServices les services nécessaires au rendu de notre page "Mvc", ainsi le HttpClient utilisé par notre application Blazor :

services.AddMvc();         
services.AddScoped<HttpClient>(s => 
{
  var navigationManager = s.GetRequiredService<NavigationManager>();
  return new HttpClient
  {
    BaseAddress = new Uri(navigationManager.BaseUri)
  };
});

Enfin dans la méthode Configure, on a besoin d'ajouter les configurations suivantes :

if (env.IsDevelopment())
{
  ...
  app.UseBlazorDebugging();
}
else
{
  app.UseHsts();
}
app.UseHttpsRedirection();
app.UseClientSideBlazorFiles<blazor_workbox_pwa.Startup>();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
  endpoints.MapDefaultControllerRoute();
  endpoints.MapFallbackToPage("/_Host");
});

On peut d'ores et déjà tester le prérendu de notre application en lançant l'application aspnetcore, et ainsi constater que le premier affichage de notre page n'affiche plus de temps de chargement :

01-blazor-server-prerendering-no-loading.gif

Bon d'accord, ce n'est peut-être pas aussi "mind blowing" que ça, mais revoyons le fonctionnement initial sans prérendu serveur :

02-blazor-no-server-prerendering-with-loading.gif

C'est mieux n'est-ce pas ?

Sur le chemin de la PWA Blazor "idéale"

Au final, après quelques points d'optimisation (activation de la compression, ajout de balises ou attributs html nécessaires, etc. plus d'infos sur GitHub), voyons voir notre score sur Lighthouse des Chrome DevTools :

03-final-chrome-audit-lighthouse-score-blazor-pwa-with-server-prerendering.png

On obtient le score maximal concernant le côté PWA de notre application ! On a toujours quelques pistes d'amélioration côté performance (correspondant au score de 85), principalement du à la taille totale de toutes nos dlls, nécessaires pour interagir avec notre app.

À ce sujet, j'ai pas mal d'espoir que la future implémentation d'une fonctionnalité de compilation AoT (Ahead of Time) améliore les performances ainsi que la taille du package. (Plus d'infos dans le ticket #5466 sur GitHub)

En conclusion

Comme toujours, j'espère que cet article vous aura donné une vue d'ensemble sur l'ajout du prérendu sur une SPA Blazor webassembly.

Merci beaucoup à Chris Sainty qui contribue beaucoup à la communauté Blazor, et notamment pour son article Prerendering a Client-side Blazor Application que je vous recommande de lire également ! Merci également à notre guru Blazor Daniel Roth qui avait partagé un exemple de prérendu serveur avec Blazor en premier-lieu sur GitHub !

N’hésitez pas à me joindre sur Twitter @vivienfabing , dans les commentaires ou par n’importe quel autre moyen, et que le code soit avec vous !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus