Surveillance des performances du blazor, JS Interop

Avec .NET 10 Previews 4 et 5, Blazor obtient des améliorations de la navigation de navigation, une prise en charge plus facile pour les composants 404 personnalisés et des améliorations d’interopération JavaScript notables.
Alors que nous nous précipitons dans la seconde moitié de l’année, les sorties de prévisualisation .NET 10 continuent de venir. Les aperçus 4 et 5 apportent de nombreux petits ajustements, mais quelques améliorations significatives également.
Voici ce qui est nouveau dans les derniers aperçus.
En savoir plus: Un tour d’horizon des trois premières versions de prévisualisation.
De nouvelles façons de surveiller les performances de votre application Blazor
Lorsque vous exécutez une application Blazor en production, il peut être difficile de diagnostiquer les problèmes de performances et de déchiffrer la façon dont vos composants fonctionnent réellement.
Dites, par exemple, vous commencez à obtenir des rapports selon lesquels votre application est «en marche lente». Sans plus de détails et de données, il vous reste à deviner ce qui pourrait causer le problème.
.NET 10 apporte une certaine visibilité à votre application Blazor et à ses performances, sous la forme de nouvelles métriques et traces.
Non, pas une chauve-souris. Ceci est un exemple de graphique montrant les performances d’une application Blazor de test sur une période de 1 minute.
Plus précisément, cela utilise le update_parameters
Métrique qui, comme son nom l’indique, suit le temps qu’il faut pour que vos composants Blazor traitent les paramètres des composants.
Dans ce cas, j’ai utilisé .NET Aspire pour visualiser les métriques et pour configurer cela, il s’agissait d’ajouter le code suivant à la configuration de la télémétrie ouverte: Open Télémétrie:
Rejoignez-nous pour une intro de la série de blogs à .NET Aspire.
builder.Services.ConfigureOpenTelemetryMeterProvider(meterProvider =>
{
meterProvider.AddMeter("Microsoft.AspNetCore.Components");
meterProvider.AddMeter("Microsoft.AspNetCore.Components.Lifecycle");
meterProvider.AddMeter("Microsoft.AspNetCore.Components.Server.Circuits");
});
builder.Services.ConfigureOpenTelemetryTracerProvider(tracerProvider =>
{
tracerProvider.AddSource("Microsoft.AspNetCore.Components");
});
builder.AddOpenTelemetryExporters();
Comme vous pouvez le voir sur les noms des compteurs ici, il existe également des mesures pour des choses telles que le nombre de circuits ouverts, actifs et / ou connectés.
Vous remarquerez qu’il y a aussi une nouvelle source de traceur. Cela signifie que vous pouvez également voir des événements de cycle de vie des composants dans les traces de votre application.
Cela peut être particulièrement utile si vous essayez de voir comment un composant spécifique interagit avec d’autres ressources (demandes de réseau par exemple) ou essayant de déchiffrer les méthodes de cycle de vie des composants lorsque et dans quel ordre.
Ces nouvelles métriques et traces sont excellentes pour l’observabilité au niveau de l’application.
Mais il y a un autre angle dont vous pourriez avoir besoin pour enquêter en ce qui concerne les performances…
NOUVEAUX MÉTriques de performance Wasm Blazor
L’un des défis de l’exécution de vos applications Web à l’aide de Blazor WebAssembly est que vous avez très peu de visibilité sur la façon dont votre application fonctionne réellement lorsqu’elle est dans le navigateur.
Nouveau dans .NET 10 sont un certain nombre d’outils de performance d’exécution de bas niveau que vous pouvez utiliser pour découvrir comment votre application fonctionne dans le navigateur.
Ces diagnostics d’exécution peuvent collecter les profils de performances du processeur, les vidages de mémoire, les compteurs de performances et les métriques et les données de performances WebAssembly natives.
Vous pouvez ensuite activer divers diagnostics via ces propriétés MSBuild:
<WasmPerfTracing>
<WasmPerfInstrumentation>
<EventSourceSupport>
<MetricsSupport>
Consultez la liste complète (et comment les configurer) dans le Notes de libération officielle ici.
Voici un exemple d’activation du traçage des performances.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<WasmPerfTracing>true</WasmPerfTracing>
</PropertyGroup>
</Project>
Il convient de noter cet avertissement des notes de publication officielle:
Notez que l’activation des diagnostics d’exécution peut avoir un impact négatif sur la taille et les performances de l’application. La publication d’une application à la production avec des profileurs n’a pas été recommandée.
Mais avec cette mise en garde à l’écart, comment accéder réellement à ces données?
Eh bien, pour cela, nous devons utiliser un peu de javascript. Avec cela, vous pouvez lancer votre application Blazor WasM dans le navigateur, puis vous diriger vers la console et exécuter le JS pertinent pour télécharger les données de performance en tant que .nettrace
déposer.
globalThis.getDotnetRuntime(0).collectCpuSamples({durationSeconds: 60});
Cela obtiendra un profil de performance en utilisant l’échantillonnage du processeur pendant 60 secondes.
Mais que se passe-t-il si vous ne souhaitez pas vous soucier d’un .nettrace
fichier et vous voulez juste voir les données de performances dans le navigateur? Pour cela, vous pouvez utiliser cette propriété MSBuild (utilisez-le au lieu de WasmPerfTracing
).
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<WasmProfilers>browser</WasmProfilers>
</PropertyGroup>
</Project>
Avec cela, vous pouvez voir ces données de performances lorsque vous vous dirigez vers l’onglet Performance (dans les outils du développeur du navigateur) et enregistrez les performances d’exécution là-bas.
Un moyen plus facile de gérer les composants / page introuvables (404S)
Une amélioration utile de la qualité de vie est que vous pouvez désormais configurer facilement une page 404 pour vos applications Blazor. Plus précisément, vous pouvez fournir un composant à utiliser dans le cas où un itinéraire n’est pas trouvé.
Routes.razor
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
Ici, nous avons défini le NotFound
paramètre à un NotFound
composant dans le Pages
dossier.
@page "/not-found"
<PageTitle>Not Found</PageTitle>
<div class="container text-center mt-5">
<h1 class="display-4">404 - Page Not Found</h1>
<p class="lead">The page you're looking for doesn't exist or has been moved.</p>
<a href="/" class="btn btn-primary mt-3">Go Home</a>
</div>
Voici comment cela sera rendu lorsque l’utilisateur tentera de visiter un itinéraire qui n’existe pas.
Un gotcha à surveiller: en ce qui concerne l’aperçu 5, vous devrez ajouter une ligne supplémentaire à programme.cs Pour que cela fonctionne.
app.UseStatusCodePagesWithReExecute("/not-found", "?statusCode={0}");
app.UseAntiforgery();
Améliorations du gestionnaire de navigation
Main dans la main avec le nouveau support du composant 404, vous pouvez désormais déclencher un 404 en utilisant NavigationManager
.
@page "/nothingtoseehere"
@inject NavigationManager NavMan
<! -- Existing Code -->
@code {
protected override void OnInitialized()
{
NavMan.NotFound();
}
}
Il convient de noter que cela ne fonctionne actuellement que pour le rendu statique du côté serveur ou les applications avec interactivité globale activée, mais la prise en charge des applications exécutant l’interactivité par composante devrait arriver dans un aperçu ultérieur.
Si vous utilisez le rendu statique du serveur, cela définira 404 comme code d’état.
Pour le rendu interactif mondial, vous pouvez définir le composant pour rendre en utilisant le nouveau NotFound
Réglage sur le routeur interactif (voir ci-dessus).
Détecter quand pas-fond déclenché
Vous pouvez également écrire votre propre code à gérer lorsque NotFound
est déclenché via NavigationManager
en utilisant le nouveau OnNotFound
méthode.
@code {
protected override void OnInitialized()
{
NavMan.OnNotFound += HandleNotFound;
NavMan.NotFound();
}
private void HandleNotFound(object? sender, NotFoundEventArgs e)
{
Console.WriteLine("Eeek, something wasn't found");
}
}
Les exceptions du gestionnaire de navigation sont parties
Un changement petit mais bienvenu est dans un ajustement à NavigationManager
.
NavigateTo
Dans .NET 8 et .NET 9 lance une exception lorsqu’elle est utilisée pendant le rendu statique du serveur. Cela s’est avéré difficile, surtout lors du débogage car vous obtenez une exception en cours d’exécution dans le rendu statique du serveur, mais le même code s’exécute sans exception lors de l’exécution de manière interactive (serveur ou wasm).
Dans les dernières versions .NET 10, cela ne se produit plus, et il se comportera de manière cohérente avec un rendu interactif, naviguant sans lancer une exception.
Améliorations significatives à JavaScript Interop
Bien que vous puissiez réaliser beaucoup avec Blazor en soi, vous aurez parfois besoin de vous tourner vers JavaScript.
JS Interopt dans les versions précédentes de .NET est assez robuste, mais avec une ou deux limitations.
Prenez, par exemple, ce code JavaScript.
wwwroot / js / calculator.js
export class Calculator {
constructor(name, precision = 2) {
this.name = name;
this.precision = precision;
this.history = [];
}
add(a, b) {
const result = parseFloat((a + b).toFixed(this.precision));
this.history.push(`${a} + ${b} = ${result}`);
return result;
}
get lastOperation() {
return this.history[this.history.length - 1] || "No operations yet";
}
clear() {
this.history = [];
}
}
Dans les versions précédentes de .NET, il était difficile de travailler avec JS comme celui-ci. Dans cet exemple, nous voulons pouvoir créer une instance de la classe JS, puis interagir avec cette instance à partir d’un composant de rasoir.
.NET 10 facilite beaucoup.
Appelez les constructeurs et les propriétés JavaScript
Ici, par exemple, est un simple composant de rasoir qui instancie, puis interagit avec la calculatrice JS (ci-dessus).
Calculatrice.razor
@page "/calculator"
@inject IJSRuntime JSRuntime
@rendermode InteractiveServer
<h2>Calculator</h2>
@if (!_isInitialized)
{
<p>Loading...</p>
}
else
{
<div>
<label for="num1">Number 1:</label>
<input id="num1" type="number" @bind="_num1"/>
</div>
<div>
<label for="num2">Number 2:</label>
<input id="num2" type="number" @bind="_num2"/>
</div>
<div>
<button @onclick="PerformAddition">Add</button>
</div>
<div>
<p>Result: @_result</p>
</div>
<div>
<button @onclick="GetLastOperation">View Last Operation</button>
</div>
<div>
<p>Last Operation: @_lastOperation</p>
</div>
}
@code {
private double _num1;
private double _num2;
private double _result;
private string _lastOperation = "No operations yet";
private IJSObjectReference? _calculatorInstance;
private bool _isInitialized;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !_isInitialized)
{
var module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/calculator.js");
_calculatorInstance = await module.InvokeNewAsync("Calculator", "TestCalculator");
_isInitialized = true;
StateHasChanged();
}
}
private async Task PerformAddition()
{
_result = await _calculatorInstance!.InvokeAsync<double>("add", _num1, _num2);
}
private async Task GetLastOperation()
{
_lastOperation = await _calculatorInstance!.GetValueAsync<string>("lastOperation");
}
}
Le code important se trouve dans ces lignes:
_calculatorInstance = await module.InvokeNewAsync("Calculator", "TestCalculator");
await _calculatorInstance!.InvokeAsync<double>("add", _num1, _num2);
Ici, nous avons pu instancier une instance de la calculatrice et y attribuer une référence à _calculatorInstance
(qui est un IJSObjectReference
).
De là, nous pouvons invoquer des fonctions sur cette instance de notre calculatrice en utilisant directement InvokeAsync
et / ou accéder aux getters en utilisant GetValueAsync
.
Obtenez des références aux fonctions JS
Vous pouvez également maintenant saisir des références aux fonctions JS, puis les transmettre comme vous le souhaitez.
Voici un exemple artificiel d’une calculatrice qui renonce à notre approche d’objet précédente et expose plutôt la logique via des fonctions.
Calculator.js (version des fonctions)
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export function dynamicOperation(callback, a, b) {
return callback(a, b);
}
Pas de classe cette fois, mais certaines fonctions que nous voulons peut-être appeler à partir de notre composant de rasoir.
Dans .NET 10, nous pouvons saisir des références à ces fonctions et les passer, comme ceci:
var addFunction = await _module.GetValueAsync<IJSObjectReference>("add");
var subtractFunction = await _module.GetValueAsync<IJSObjectReference>("subtract");
Ces références de fonction sont désormais disponibles dans notre composant de rasoir, prête à faire avec ce que nous voulons.
Par exemple, nous pouvons décider lequel d’entre eux utiliser, puis passer celui choisi à cela dynamicOperation
fonction que nous avons vu dans le javascript (ci-dessus).
Calculatrice.razor
@code {
private string _selectedOperation = "add";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !_isInitialized)
{
_module = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/calculator.js");
}
}
private async Task UseDynamicOperationCallback()
{
if (_module != null)
{
var addFunction = await _module.GetValueAsync<IJSObjectReference>("add");
var subtractFunction = await _module.GetValueAsync<IJSObjectReference>("subtract");
var selectedFunction = _selectedOperation.ToLower() switch
{
"add" => addFunction,
"subtract" => subtractFunction,
_ => throw new ArgumentException("Invalid operation")
};
_result = await _module.InvokeAsync<double>("dynamicOperation", selectedFunction, _num1, _num2);
}
}
}
Cela crée des références aux fonctions ADD et soustrayantes. Il détermine alors s’il faut utiliser le subtract
ou add
Fonction (nous pouvons supposer que ce composant permet à l’utilisateur de choisir lequel).
Enfin, il transmet cette référence de fonction comme un rappel lors de l’invoquer le dynamicOperation
Fonction dans notre module JS.
Autres améliorations notables
Il y a aussi quelques autres changements.
Les actifs statiques du blazor sont désormais automatiquement préchargés. Dans les versions antérieures de .NET, le navigateur téléchargerait la page HTML pour votre site, analyser le HTML pour découvrir les fichiers JS / Wasm dont il avait besoin, puis les télécharger avant de démarrer votre application. Maintenant, en utilisant Link
En-têtes, le navigateur est invité à commencer à charger ces ressources avant même que la page initiale de récupérer et le rendu ne se produise.
Le Blazor webassembly Modèle d’application autonome a reçu quelques mises à jour. Notamment les modifications nécessaires pour précharger les actifs du cadre, générer une carte d’importation JavaScript et activer les empreintes digitales pour Blazor.webassembly.js (afin que les utilisateurs ne se retrouvent avec une version périmée lorsqu’elle change).
Le Bozor Boot Manifest a été fusionné dans dotnet.js
. et n’est plus un fichier séparé (blazor.boot.json
), pour réduire le nombre de demandes HTTP et améliorer les performances.
En résumé
Améliorations du gestionnaire de navigation, prise en charge plus facile pour les composants 404 personnalisés et les améliorations notables de l’interopération JavaScript conduisent la charge dans .NET 10 Aperçu 4 et 5.
.NET 10 reste sur la bonne voie pour sa sortie en novembre 2025.
Source link