ASP.NET Core Razor komponentens livscykel
Anteckning
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Den här artikeln beskriver ASP.NET Core Razor komponentens livscykel och hur du använder livscykelhändelser.
Livscykelhändelser
Den Razor komponenten bearbetar Razor komponentens livscykelhändelser i en uppsättning synkrona och asynkrona livscykelmetoder. Livscykelmetoderna kan åsidosättas för att utföra ytterligare åtgärder i komponenter under komponentinitiering och återgivning.
Den här artikeln förenklar händelsebearbetningen av komponentens livscykel för att förtydliga komplex ramverkslogik och omfattar inte alla ändringar som har gjorts under åren. Du kan behöva komma åt ComponentBase
referenskällan för att integrera anpassad händelsebearbetning med Blazorlivscykelhändelsebearbetning. Kodkommentarer i referenskällan innehåller ytterligare kommentarer om livscykelhändelsebearbetning som inte visas i den här artikeln eller i API-dokumentationen.
Anteckning
Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik release använder du listrutan för att växla mellan grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).
Följande förenklade diagram illustrerar händelsebearbetning för Razor komponentens livscykel. De C#-metoder som är associerade med livscykelhändelserna definieras med exempel i följande avsnitt i den här artikeln.
Händelser i komponentens livscykel:
- Om komponenten renderar för första gången vid en förfrågan:
- Skapa komponentens instans.
- Utför egenskapsinmatning.
- Ringa
OnInitialized{Async}
. Om en ofullständig Task returneras, avvaktar man Task och sedan återskapas komponenten. Den synkrona metoden anropas före den asynkrona metoden.
- Ringa
OnParametersSet{Async}
. Om en ofullständig Task returneras, avvaktar man Task och sedan återskapas komponenten. Den synkrona metoden anropas före den asynkrona metoden. - Rendera för allt synkront arbete och slutför uppgift Tasks.
Anteckning
Asynkrona åtgärder som utförs i livscykelhändelser kan fördröja komponentrendering eller visning av data. Mer information finns i avsnittet Hantera ofullständiga asynkrona åtgärder vid återgivning senare i den här artikeln.
En föräldrakomponent renderas före sina barnkomponenter eftersom rendering bestämmer vilka barnkomponenter som finns. Om synkron initiering av överordnad komponent används kommer den överordnade initieringen garanterat att slutföras först. Om asynkron initiering av överordnad komponent används kan inte slutförandeordningen för initiering av överordnad och underordnad komponent fastställas eftersom den är beroende av att initieringskoden körs.
DOM-händelsebearbetning:
- Händelsehanteraren körs.
- Om en ofullständig Task returneras, avvaktar man Task och sedan återskapas komponenten.
- Rendera för allt synkront arbete och slutför uppgift Tasks.
Livscykeln för Render
:
- Undvik ytterligare återgivningsåtgärder på komponenten när båda följande villkor uppfylls:
- Det är inte den första renderingen.
-
ShouldRender
returnerarfalse
.
- Skapa skillnaden (diff) i återgivningsträdet och rendera komponenten.
- Vänta på att DOM uppdateras.
- Ringa
OnAfterRender{Async}
. Den synkrona metoden anropas före den asynkrona metoden.
Utvecklaranrop till StateHasChanged
resulterar i en omarendering. För mer information, se ASP.NET Core Razor komponentåtergivning.
Viloläge vid föråtergivning
I Blazor appar på serversidan väntar prerendering på quiescence, vilket innebär att en komponent inte renderas förrän alla komponenter i återgivningsträdet har slutfört renderingen. Quiescence kan leda till märkbara fördröjningar i renderingen när en komponent utför långvariga uppgifter under initiering och andra livscykelmetoder, vilket leder till en dålig användarupplevelse. Mer information finns i avsnittet Hantera ofullständiga asynkrona åtgärder vid återgivning senare i den här artikeln.
När parametrar anges (SetParametersAsync
)
SetParametersAsync anger parametrar som tillhandahålls av komponentens överordnade i återgivningsträdet eller från vägparametrar.
Metodens ParameterView-parameter innehåller uppsättningen komponentparametern värden för komponenten varje gång SetParametersAsync anropas. Genom att åsidosätta metoden SetParametersAsync kan utvecklarkod interagera direkt med ParameterViewparametrar.
Standardimplementeringen av SetParametersAsync anger värdet för varje egenskap med attributet [Parameter]
eller [CascadingParameter]
som har ett motsvarande värde i ParameterView. Parametrar som inte har ett motsvarande värde i ParameterView lämnas oförändrade.
I allmänhet bör koden anropa basklassmetoden (await base.SetParametersAsync(parameters);
) när du åsidosättar SetParametersAsync. I avancerade scenarier kan utvecklarkod tolka de inkommande parametrarnas värden på alla sätt som krävs genom att inte anropa basklassmetoden. Det finns till exempel inget krav på att tilldela inkommande parametrar till egenskaperna för klassen. Du måste dock referera till ComponentBase
referenskälla när du strukturerar koden utan att anropa basklassmetoden eftersom den anropar andra livscykelmetoder och utlöser rendering på ett komplext sätt.
Anteckning
Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik release använder du listrutan för att växla mellan grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).
Om du vill förlita dig på initierings- och renderingslogik för ComponentBase.SetParametersAsync men inte bearbeta inkommande parametrar kan du skicka en tom ParameterView till basklassmetoden:
await base.SetParametersAsync(ParameterView.Empty);
Om händelsehanterare anges i utvecklarkod, ta bort dem när de inte längre behövs. Mer information finns i ASP.NET Core Razor komponenthantering.
I följande exempel tilldelar ParameterView.TryGetValueParam
-parameterns värde till value
om parsning av en routningsparameter för Param
lyckas. När value
inte är null
visas värdet av komponenten.
Även om vägparametermatchning är skiftlägesokänsligtmatchar TryGetValue endast skiftlägeskänsliga parameternamn i routningsmallen. Följande exempel kräver användning av /{Param?}
i routningsmallen för att hämta värdet med TryGetValue, inte /{param?}
. Om /{param?}
används i det här scenariot returnerar TryGetValuefalse
och message
är inte inställt på någon av message
strängarna.
SetParamsAsync.razor
:
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async"
@page "/set-params-async/{Param}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
Komponentinitiering (OnInitialized{Async}
)
OnInitialized och OnInitializedAsync används uteslutande för att initiera en komponent under hela komponentinstansens livslängd. Parametervärden och parametervärdeändringar bör inte påverka initieringen som utförs i dessa metoder. Till exempel utförs inläsning av statiska alternativ i en listruta som inte ändras under komponentens livslängd och som inte är beroende av parametervärden i någon av dessa livscykelmetoder. Om parametervärden eller ändringar i parametervärden påverkar komponenttillståndet använder du OnParametersSet{Async}
i stället.
Dessa metoder anropas när komponenten initieras efter att ha tagit emot de första parametrarna i SetParametersAsync. Den synkrona metoden anropas före den asynkrona metoden.
Om synkron initiering av överordnad komponent används kommer den överordnade initieringen garanterat att slutföras innan den underordnade komponenten initieras. Om asynkron initiering av överordnad komponent används kan inte slutförandeordningen för initiering av överordnad och underordnad komponent fastställas eftersom den är beroende av att initieringskoden körs.
För en synkron åtgärd åsidosätter du OnInitialized:
OnInit.razor
:
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Om du vill utföra en asynkron åtgärd åsidosätter du OnInitializedAsync och använder operatorn await
:
protected override async Task OnInitializedAsync()
{
await ...
}
Om en anpassad basklass används med anpassad initieringslogik anropar du OnInitializedAsync på basklassen:
protected override async Task OnInitializedAsync()
{
await ...
await base.OnInitializedAsync();
}
Det är inte nödvändigt att anropa ComponentBase.OnInitializedAsync om inte en anpassad basklass används med anpassad logik. Mer information finns i avsnittet Livscykelmetoder för basklass.
En komponent måste se till att den är i ett giltigt tillstånd för återgivning när OnInitializedAsync väntar på en potentiellt ofullständig Task. Om metoden returnerar en ofullständig Taskmåste den del av metoden som slutförs synkront lämna komponenten i ett giltigt tillstånd för återgivning. Mer information finns i introduktionskommentarerna för ASP.NET Core Blazor-synkroniseringskontext och ASP.NET Core Razor komponenthantering.
Blazor appar som förbereder sitt innehåll på servern anropar OnInitializedAsynctvå gånger:
- En gång när komponenten ursprungligen återges statiskt som en del av sidan.
- En andra gång när webbläsaren återger komponenten.
För att förhindra att utvecklarkod i OnInitializedAsync körs två gånger vid förhandsrendering, se avsnittet Stateful-återanslutning efter förhandsrendering. Innehållet i avsnittet fokuserar på Blazor Web App:n och tillståndsbevarande SignalRåteranslutning. Information om hur du bevarar tillståndet under exekveringen av initieringskoden vid prerendering finns i Prerender ASP.NET Core Razor-komponenter.
För att förhindra att utvecklarkod i OnInitializedAsync körs två gånger vid förhandsrendering, se avsnittet Stateful-återanslutning efter förhandsrendering. Även om innehållet i avsnittet fokuserar på
När en Blazor app förbereder rendering är vissa åtgärder, till exempel anrop till JavaScript (JS interop), inte möjliga. Komponenter kan behöva renderas annorlunda när de är för-renderade. Mer information finns i avsnittet Prerendering med JavaScript interop.
Om händelsehanterare anges i utvecklarkod, ta bort dem när de inte längre behövs. Mer information finns i ASP.NET Core Razor komponenthantering.
Använd strömningsrendering med statisk återgivning på serversidan (statisk SSR) eller förhandsrendering för att förbättra användarupplevelsen för komponenter som utför långvariga asynkrona uppgifter i OnInitializedAsync för att återges fullständigt. Mer information finns i följande resurser:
När parametrar har angetts (OnParametersSet{Async}
)
OnParametersSet eller OnParametersSetAsync anropas:
När komponenten har initierats i OnInitialized eller OnInitializedAsync.
När den överordnade komponenten skickar om och tillhandahåller:
- Kända eller primitiva oföränderliga typer när minst en parameter har ändrats.
- Parametrar av komplexa datatyper. Ramverket kan inte veta om värdena för en komplex typad parameter har muterats internt, så ramverket behandlar alltid parameteruppsättningen som ändrad när en eller flera komplexa parametrar finns.
Mer information om återgivningskonventioner finns i ASP.NET Core Razor komponentåtergivning.
Den synkrona metoden anropas före den asynkrona metoden.
Metoderna kan anropas även om parametervärdena inte har ändrats. Det här beteendet understryker behovet av att utvecklare implementerar ytterligare logik inom metoderna för att kontrollera om parametervärdena verkligen har ändrats innan data eller tillstånd som är beroende av dessa parametrar initieras på nytt.
För följande exempelkomponent navigerar du till komponentens sida på en URL:
- Med ett startdatum som tas emot av
StartDate
:/on-parameters-set/2021-03-19
- Utan startdatum, där
StartDate
tilldelas ett värde för den aktuella lokala tiden:/on-parameters-set
Anteckning
I en komponentväg är det inte möjligt att både begränsa en DateTime parameter med vägvillkor datetime
och göra parametern valfri. Därför använder följande OnParamsSet
komponent två @page
-direktiv för att hantera routning med och utan ett angivet datumsegment i URL:en.
OnParamsSet.razor
:
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
Asynkront arbete vid tillämpning av parametrar och egenskapsvärden måste ske under OnParametersSetAsync livscykelhändelse:
protected override async Task OnParametersSetAsync()
{
await ...
}
Om en anpassad basklass används med anpassad initieringslogik anropar du OnParametersSetAsync på basklassen:
protected override async Task OnParametersSetAsync()
{
await ...
await base.OnParametersSetAsync();
}
Det är inte nödvändigt att anropa ComponentBase.OnParametersSetAsync om inte en anpassad basklass används med anpassad logik. Mer information finns i avsnittet Livscykelmetoder för basklass.
En komponent måste se till att den är i ett giltigt tillstånd för återgivning när OnParametersSetAsync väntar på en potentiellt ofullständig Task. Om metoden returnerar en ofullständig Taskmåste den del av metoden som slutförs synkront lämna komponenten i ett giltigt tillstånd för återgivning. Mer information finns i introduktionskommentarerna för ASP.NET Core Blazor-synkroniseringskontext och ASP.NET Core Razor komponenthantering.
Om händelsehanterare anges i utvecklarkod, ta bort dem när de inte längre behövs. Mer information finns i ASP.NET Core Razor komponenthantering.
Om en engångskomponent inte använder en CancellationTokenbör OnParametersSet och OnParametersSetAsync kontrollera om komponenten tas bort. Om OnParametersSetAsync returnerar en ofullständig Taskmåste komponenten se till att den del av metoden som slutförs synkront lämnar komponenten i ett giltigt tillstånd för återgivning. Mer information finns i introduktionskommentarerna för ASP.NET Core Blazor-synkroniseringskontext och ASP.NET Core Razor komponenthantering.
Mer information om routningsparametrar och begränsningar finns i ASP.NET Core Blazor routning och navigering.
Ett exempel på hur du implementerar SetParametersAsync
manuellt för att förbättra prestanda i vissa scenarier finns i bästa praxis för ASP.NET Core Blazor prestanda.
Efter komponentrendering (OnAfterRender{Async}
)
OnAfterRender och OnAfterRenderAsync anropas när en komponent har renderats interaktivt och användargränssnittet har uppdaterats (till exempel efter att element har lagts till i webbläsarens DOM). Element- och komponentreferenser fylls i just nu. Använd den här fasen för att utföra ytterligare initieringssteg med det renderade innehållet, till exempel JS interop-anrop som interagerar med de renderade DOM-elementen. Den synkrona metoden anropas före den asynkrona metoden.
Dessa metoder anropas inte under förrendering eller statisk återgivning på serversidan (statisk SSR) på servern eftersom dessa processer inte är kopplade till en realtidswebbläsares DOM och redan är slutförda innan DOM uppdateras.
För OnAfterRenderAsync återges inte komponenten automatiskt när någon returnerad Task
har slutförts, för att undvika en oändlig återgivningsloop.
OnAfterRender och OnAfterRenderAsync anropas när en komponent har slutfört renderingen. Element- och komponentreferenser fylls i just nu. Använd den här fasen för att utföra ytterligare initieringssteg med det renderade innehållet, till exempel JS interop-anrop som interagerar med de renderade DOM-elementen. Den synkrona metoden anropas före den asynkrona metoden.
Dessa metoder anropas inte under prerendering eftersom prerendering inte är kopplat till en live-webbläsar-DOM och redan är klar innan DOM uppdateras.
För OnAfterRenderAsyncåterställs inte komponenten automatiskt efter slutförandet av returnerade Task
för att undvika en oändlig återgivningsloop.
Parametern firstRender
för OnAfterRender och OnAfterRenderAsync:
- Anges till
true
första gången komponentinstansen återges. - Kan användas för att säkerställa att initieringsarbetet bara utförs en gång.
AfterRender.razor
:
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
Det AfterRender.razor
exemplet genererar följande utdata till konsolen när sidan läses in och knappen är markerad:
OnAfterRender: firstRender = True
HandleClick called
OnAfterRender: firstRender = False
Asynkront arbete måste ske under livscykelhändelsen OnAfterRenderAsync omedelbart efter återgivningen.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
}
Om en anpassad basklass används med anpassad initieringslogik anropar du OnAfterRenderAsync på basklassen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
await base.OnAfterRenderAsync(firstRender);
}
Det är inte nödvändigt att anropa ComponentBase.OnAfterRenderAsync om inte en anpassad basklass används med anpassad logik. Mer information finns i avsnittet Livscykelmetoder för basklass.
Även om du returnerar en Task från OnAfterRenderAsyncschemalägger ramverket inte ytterligare en återgivningscykel för komponenten när aktiviteten har slutförts. Detta är för att undvika en oändlig återgivningsloop. Detta skiljer sig från de andra livscykelmetoderna, som schemalägger ytterligare en återgivningscykel när en returnerad Task har slutförts.
OnAfterRender och OnAfterRenderAsyncanropas inte under förrenderingsprocessen på servern. Metoderna anropas när komponenten återges interaktivt efter förinläsning. När appen renderar i förväg:
- Komponenten körs på servern för att skapa en statisk HTML-kod i HTTP-svaret. Under den här fasen anropas inte OnAfterRender och OnAfterRenderAsync.
- När Blazor skriptet (
blazor.{server|webassembly|web}.js
) startar i webbläsaren startas komponenten om i ett interaktivt återgivningsläge. När en komponent har startats om anropas OnAfterRender och OnAfterRenderAsync eftersom appen inte längre är i prerenderingsfasen.
Om händelsehanterare anges i utvecklarkod, ta bort dem när de inte längre behövs. Mer information finns i ASP.NET Core Razor komponenthantering.
Livscykelmetoder för basklass
När du åsidosätter Blazor:s livscykelmetoder är det inte nödvändigt att anropa basklassens livscykelmetoder för ComponentBase. En komponent bör dock anropa en åsidosatt basklasslivscykelmetod i följande situationer:
- När du åsidosätter ComponentBase.SetParametersAsyncanropas vanligtvis
await base.SetParametersAsync(parameters);
eftersom basklassmetoden anropar andra livscykelmetoder och utlöser rendering på ett komplext sätt. Mer information finns i avsnittet När parametrar har angetts (SetParametersAsync
). - Om basklassmetoden innehåller logik som måste köras. Bibliotekskonsumenter anropar vanligtvis livscykelmetoder för basklass när de ärver en basklass eftersom biblioteksbasklasser ofta har anpassad livscykellogik att köra. Om appen använder en basklass från ett bibliotek kan du läsa bibliotekets dokumentation för vägledning.
I följande exempel anropas base.OnInitialized();
för att säkerställa att basklassens OnInitialized
-metod körs. Utan anropet körs inte BlazorRocksBase2.OnInitialized
.
BlazorRocks2.razor
:
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
BlazorRocksBase2.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
Tillståndsändringar (StateHasChanged
)
StateHasChanged meddelar komponenten att dess tillstånd har ändrats. Om det är tillämpligt, anropar StateHasChanged en ny rendering som inträffar när appens huvudtråd är fri.
StateHasChanged anropas automatiskt för EventCallback metoder. Mer information om händelseåteranrop finns i ASP.NET Core Blazor händelsehantering.
Mer information om komponentrendering och när du ska anropa StateHasChanged, inklusive när du ska anropa den med ComponentBase.InvokeAsync, finns i ASP.NET Core Razor komponentåtergivning.
Hantera ofullständiga asynkrona åtgärder vid återgivning
Asynkrona handlingar som utförs under livscykelhändelser kanske inte har slutförts innan komponenten återges. Objekt kan vara null
eller ofullständigt ifyllda med data medan livscykelmetoden körs. Ange renderingslogik för att bekräfta att objekt initieras. Visa platshållarelement (till exempel ett inläsningsmeddelande) medan objekten laddas null
.
I följande Slow
-komponent åsidosätts OnInitializedAsync för att asynkront köra en långvarig uppgift. När isLoading
är true
visas ett inläsningsmeddelande för användaren. När Task
, som returneras av OnInitializedAsync, har slutförts renderas komponenten på nytt i sitt uppdaterade tillstånd och visar meddelandet "Finished!
".
Slow.razor
:
@page "/slow"
<h2>Slow Component</h2>
@if (isLoading)
{
<div><em>Loading...</em></div>
}
else
{
<div>Finished!</div>
}
@code {
private bool isLoading = true;
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
isLoading = false;
}
private Task LoadDataAsync()
{
return Task.Delay(10000);
}
}
Föregående komponent använder en isLoading
variabel för att visa inläsningsmeddelandet. En liknande metod används för en komponent som läser in data i en samling och kontrollerar om samlingen är null
för att presentera inläsningsmeddelandet. Det följande exemplet kontrollerar movies
-samlingen för null
för att antingen visa inläsningsmeddelandet eller visa filmsamlingen:
@if (movies == null)
{
<p><em>Loading...</em></p>
}
else
{
@* display movies *@
}
@code {
private Movies[]? movies;
protected override async Task OnInitializedAsync()
{
movies = await GetMovies();
}
}
Prerendering väntar på quiescence, vilket innebär att en komponent inte renderas förrän alla komponenter i återgivningsträdet har slutfört renderingen. Det innebär att ett inläsningsmeddelande inte visas medan en underordnad komponents OnInitializedAsync-metod kör en tidskrävande uppgift under förinläsningen. Om du vill demonstrera det här beteendet placerar du den föregående Slow
komponenten i testappens Home
komponent:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SlowComponent />
Anteckning
Även om exemplen i det här avsnittet beskriver OnInitializedAsync livscykelmetod kan andra livscykelmetoder som körs under förinläsning fördröja den slutliga återgivningen av en komponent. Endast OnAfterRender{Async}
utförs inte under prerendering och påverkas inte av fördröjningar på grund av stillestånd.
Under förrendering renderas inte Home
-komponenten förrän den Slow
komponenten återges, vilket tar tio sekunder. Användargränssnittet är tomt under den här tio sekunder långa perioden och det finns inget inläsningsmeddelande. Efter förrendering renderas Home
-komponenten och Slow
-komponentens inläsningsmeddelande visas. Efter ytterligare tio sekunder visar Slow
komponenten slutligen det färdiga meddelandet.
Som föregående demonstration visar, resulterar passivitet under förberendering i en dålig användarupplevelse. För att förbättra användarupplevelsen börjar du med att implementera strömningsrendering för att undvika att vänta på att den asynkrona uppgiften ska slutföras under prerendering.
Lägg till attributet [StreamRendering]
till komponenten Slow
(använd [StreamRendering(true)]
i .NET 8):
@attribute [StreamRendering]
När Home
-komponenten förhandsrenderas, återges Slow
-komponenten snabbt med sitt laddningsmeddelande. Komponenten Home
väntar inte i tio sekunder på att den Slow
komponenten ska slutföra återgivningen. Det färdiga meddelandet som visas i slutet av prerendering ersätts dock av inläsningsmeddelandet medan komponenten slutligen renderas, vilket är ytterligare tio sekunders fördröjning. Detta beror på att komponenten Slow
renderas två gånger och att LoadDataAsync
också körs två gånger. När en komponent kommer åt resurser, till exempel tjänster och databaser, skapar dubbel körning av tjänstanrop och databasfrågor oönskad belastning på appens resurser.
För att hantera dubbel återgivning av inläsningsmeddelandet och återkörningen av tjänst- och databasanrop: spara förinläst tillstånd med PersistentComponentState för den slutliga återgivningen av komponenten, enligt följande uppdateringar av komponenten Slow
:
@page "/slow"
@attribute [StreamRendering]
@implements IDisposable
@inject PersistentComponentState ApplicationState
<h2>Slow Component</h2>
@if (data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@data</div>
}
@code {
private string? data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
persistingSubscription =
ApplicationState.RegisterOnPersisting(PersistData);
if (!ApplicationState.TryTakeFromJson<string>("data", out var restored))
{
data = await LoadDataAsync();
}
else
{
data = restored!;
}
}
private Task PersistData()
{
ApplicationState.PersistAsJson("data", data);
return Task.CompletedTask;
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(10000);
return "Finished!";
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
Genom att kombinera strömningsrendering med beständiga komponenttillstånd:
- Tjänster och databaser kräver bara ett enda anrop för komponentinitiering.
- Komponenter renderar sina användargränssnitt snabbt och visar laddningsmeddelanden under långvariga uppgifter för att ge den bästa användarupplevelsen.
Mer information finns i följande resurser:
Inaktivitet under föråtergivning resulterar i en dålig användarupplevelse. Fördröjningen kan åtgärdas i appar som riktar in sig på .NET 8 eller senare med en funktion som kallas strömningsrendering, vanligtvis kombinerad med bestående komponenttillstånd under förrendering för att undvika att vänta på att den asynkrona uppgiften ska slutföras. I versioner av .NET tidigare än 8.0 kan en tidskrävande bakgrundsuppgift som laddar in data efter den slutliga återgivningen lösa en lång återgivningsfördröjning orsakat av stillestånd.
Hantera felhantering
Information om hur du hanterar fel vid körning av livscykelmetoder finns i Hantera fel i ASP.NET Core Blazor-appar.
Tillståndskänslig återanslutning efter förhandsrendering
Vid prerendering på servern återges en komponent ursprungligen statiskt som en del av sidan. När webbläsaren har upprättat en SignalR anslutning tillbaka till servern blir komponenten återigen och interaktiv. Om OnInitialized{Async}
livscykelmetod för att initiera komponenten finns körs metoden två gånger:
- När komponenten prerendereras statiskt.
- När serveranslutningen har upprättats.
Detta kan resultera i en märkbar ändring i de data som visas i användargränssnittet när komponenten slutligen återges. Undvik det här beteendet genom att skicka in en identifierare för att cachelagra tillståndet under prerendering och hämta tillståndet efter förinläsning.
Följande kod visar en WeatherForecastService
som undviker ändringar i datavisningen på grund av prerendering. Den väntade Delay (await Task.Delay(...)
) simulerar en kort fördröjning innan data returneras från metoden GetForecastAsync
.
Tillför IMemoryCache tjänster med AddMemoryCache i tjänstesamlingen i appens Program
-fil:
builder.Services.AddMemoryCache();
WeatherForecastService.cs
:
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
Mer information om RenderModefinns i ASP.NET Core BlazorSignalR vägledning.
Innehållet i det här avsnittet fokuserar på Blazor Web Apps och tillståndsbaserad SignalRåteranslutning. Information om hur du bevarar tillståndet under exekveringen av initieringskoden vid prerendering finns i Prerender ASP.NET Core Razor-komponenter.
Även om innehållet i det här avsnittet fokuserar på Blazor Server och tillståndsbaserad SignalRåteranslutning, omfattar scenariot för förhandsrendering i värdbaserade Blazor WebAssembly-lösningar (WebAssemblyPrerendered) liknande villkor och metoder för att förhindra att utvecklarkod körs två gånger. Information om hur du bevarar tillståndet under körningen av initieringskoden vid förberedande rendering finns i Integrera ASP.NET Core Razor-komponenter med MVC eller Razor-sidor.
Prerendering med JavaScript-interop
Det här avsnittet gäller för appar på serversidan som förberender Razor-komponenter. Prerendering beskrivs i komponenterna Prerender ASP.NET Core Razor.
Anteckning
Intern navigering för interaktiv routning i Blazor Web Appinnebär inte att begära nytt sidinnehåll från servern. Därför sker inte prerendering för interna sidbegäranden. Om appen använder interaktiv ruttning, utför en fullständig sidinläsning för komponentexempel som visar förgenereringsbeteende. Mer information finns i Prerender ASP.NET Core Razor-komponenter.
Det här avsnittet gäller för appar på serversidan och värdbaserade Blazor WebAssembly-appar som förbereder Razor-komponenter. Förrendering beskrivs i Integrera ASP.NET Core Razor-komponenter med MVC eller Razor Pages.
Det går inte att anropa JavaScript (JS) under förinläsningen. I följande exempel visas hur du använder JS interop som en del av en komponents initieringslogik på ett sätt som är kompatibelt med prerendering.
Följande scrollElementIntoView
funktion:
- Rullar till det överförda elementet med
scrollIntoView
. - Returnerar elementets
top
egenskapsvärde från metodengetBoundingClientRect
.
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Om IJSRuntime.InvokeAsync anropar funktionen JS i komponentkoden används ElementReference endast i OnAfterRenderAsync och inte i någon tidigare livscykelmetod eftersom det inte finns något HTML DOM-element förrän komponenten har renderats.
StateHasChanged
(referenskälla) anropas för att ange omrendering av komponenten med det nya tillståndet som erhålls från JS interop-anropet (mer information finns i ASP.NET Core Razor komponentrendering). En oändlig loop skapas inte eftersom StateHasChanged bara anropas när scrollPosition
är null
.
PrerenderedInterop.razor
:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
Föregående exempel förorenar klienten med en global funktion. En bättre metod i produktionsappar finns i JavaScript-isolering i JavaScript-moduler.
Avbrytbar bakgrundsarbete
Komponenter utför ofta tidskrävande bakgrundsarbete, till exempel att göra nätverksanrop (HttpClient) och interagera med databaser. Det är önskvärt att stoppa bakgrundsarbetet för att spara systemresurser i flera situationer. Till exempel stoppas inte asynkrona bakgrundsåtgärder automatiskt när en användare navigerar bort från en komponent.
Andra orsaker till att bakgrundsarbetsobjekt kan kräva annullering är:
- En körande bakgrundsaktivitet startades med felaktiga indata eller bearbetningsparametrar.
- Den aktuella uppsättningen av bakgrundsprocesser måste ersättas med en ny uppsättning arbetsuppgifter.
- Prioriteten för pågående uppgifter måste ändras.
- Appen måste stängas av för omdistribution av servern.
- Serverresurserna blir begränsade, vilket gör det nödvändigt att schemalägga om bakgrundsarbetsobjekt.
Så här implementerar du ett avbrytbar bakgrundsarbete i en komponent:
- Använd en CancellationTokenSource och CancellationToken.
- När bortskaffande av komponenten och när som helst annullering önskas genom att manuellt avbryta token anropar du
CancellationTokenSource.Cancel
för att signalera att bakgrundsarbetet ska avbrytas. - När det asynkrona anropet har returnerats, ska du anropa ThrowIfCancellationRequested på token.
I följande exempel:
-
await Task.Delay(10000, cts.Token);
representerar tidskrävande asynkront bakgrundsarbete. -
BackgroundResourceMethod
representerar en långvarig bakgrundsmetod som inte bör starta omResource
tas bort innan metoden anropas.
BackgroundWork.razor
:
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new Resource();
private CancellationTokenSource cts = new CancellationTokenSource();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
Blazor Server återanslutningshändelser
Komponenternas livscykelhändelser som beskrivs i den här artikeln fungerar separat från händelsehanterare för återanslutning på serversidan. När SignalR-anslutningen till klienten går förlorad avbryts endast uppdateringar av användargränssnittet. Uppdateringar av användargränssnittet återupptas när anslutningen återupprättas. Mer information om kretshanterarhändelser och konfiguration finns i ASP.NET Core BlazorSignalR vägledning.
Ytterligare resurser
Hantera fångade undantag utanför en Razor komponents livscykel
ASP.NET Core