Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Upozornění
Tato verze ASP.NET Core již není podporována. Pro více informací si přečtěte zásady podpory .NET a .NET Core. Pro aktuální verzi si přečtěte článek o verzi .NET 9.
Tento článek vysvětluje životní cyklus komponenty ASP.NET Core Razor a jak používat události životního cyklu.
Události životního cyklu
Komponenta Razor zpracovává události životního cyklu komponenty Razor v sadě synchronních a asynchronních metod životního cyklu. Metody životního cyklu mohou být přepsány, aby prováděly další operace v komponentách během inicializace a vykreslování komponent.
Tento článek zjednodušuje zpracování událostí životního cyklu komponentů, aby objasnil složitou logiku frameworku, a nezahrnuje každou změnu, která byla provedena v průběhu let. Možná budete potřebovat přístup ke zdrojovým referencím ComponentBase, aby bylo možné integrovat zpracování vlastních událostí se zpracováním událostí životního cyklu Blazor. Komentáře ke kódu v referenčním zdroji zahrnují další poznámky k zpracování událostí životního cyklu, které se neobjevují v tomto článku ani v dokumentaci API.
Poznámka:
Odkazy na dokumentaci ke zdrojům pro .NET obvykle načítají výchozí větev úložiště, která reprezentuje aktuální vývoj pro další vydání .NET. K výběru značky pro konkrétní vydání použijte rozevírací seznam Přepnout větve nebo značky. Pro více informací si přečtěte Jak vybrat verzi štítku zdrojového kódu ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Následující zjednodušené diagramy ilustrují zpracování událostí životního cyklu komponenty Razor. Metody C# související s událostmi životního cyklu jsou definovány v následujících částech tohoto článku spolu s příklady.
Události životního cyklu komponenty
- Pokud se komponenta vykresluje poprvé na žádost:
- Vytvořte instanci komponenty.
- Proveďte vkládání vlastností.
- Zavolat
OnInitialized{Async}. Pokud je vrácena neúplná Task, vyčká se na Task a poté se komponent znovu vykreslí. Synchronní metoda je volána před asynchronní metodou.
- Zavolat
OnParametersSet{Async}. Pokud je vrácena neúplná Task, vyčká se na Task a poté se komponent znovu vykreslí. Synchronní metoda je volána před asynchronní metodou. - Zpracujte veškerou synchronní práci a dokončete Tasks.
Poznámka:
Asynchronní akce prováděné v událostech životního cyklu můžou zpožďovat vykreslování komponent nebo zobrazovat data. Další informace najdete v části Zpracování neúplných asynchronních akcí při vykreslování dále v tomto článku.
Nadřazená komponenta se vykreslí před komponentami potomků, protože vykreslení rozhoduje o tom, které komponenty potomků jsou přítomny. Pokud je použita synchronní inicializace nadřazené komponenty, je zaručeno, že inicializace nadřazené komponenty se dokončí jako první. Pokud se používá asynchronní inicializace nadřazené komponenty, nelze určit pořadí dokončení inicializace nadřazené a podřízené komponenty, protože závisí na spuštění inicializačního kódu.
Zpracování událostí DOM:
- Obsluha události se spustila.
- Pokud je vrácena neúplná Task, vyčká se na Task a poté se komponent znovu vykreslí.
- Zpracujte veškerou synchronní práci a dokončete Tasks.
Životní cyklus Render:
- Neprovádějte další operace vykreslování na komponentě, pokud jsou splněny obě následující podmínky:
- Není to první vykreslení.
-
ShouldRendervracífalse.
- Sestavte rozdíl mezi stromy vykreslování a vykreslete komponentu.
- Počkejte, až se DOM aktualizuje.
- Zavolat
OnAfterRender{Async}. Synchronní metoda je volána před asynchronní metodou.
Volání vývojáře na StateHasChanged má za následek opětovné vykreslení. Pro více informací viz ASP.NET Core Razor component rendering.
Klid během předběžného vykreslování
V aplikacích na straně serveru Blazor předrenderování čeká na klid , což znamená, že se komponenta nevykreslí, dokud se všechny komponenty ve stromu vykreslování nedokončí vykreslování. Klid může vést ke znatelným zpožděním při vykreslování, když komponenta provádí dlouhotrvající úlohy během inicializace a dalších metod životního cyklu, což vede k špatnému uživatelskému zážitku. Další informace najdete v části Zpracování neúplných asynchronních akcí při vykreslování dále v tomto článku.
Když jsou nastaveny parametry (SetParametersAsync)
SetParametersAsync nastavuje parametry zadané rodičovskou komponentou ve vykreslovacím stromu nebo z parametrů trasy.
Parametr metody ParameterView obsahuje množinu hodnot parametru komponenty pro komponentu pokaždé, když je zavolána metoda SetParametersAsync. Přepsáním metody SetParametersAsync může kód vývojáře přímo spolupracovat s parametry ParameterView.
Výchozí implementace SetParametersAsync nastaví hodnotu každé vlastnosti pomocí atributu [Parameter] nebo [CascadingParameter], který má odpovídající hodnotu v ParameterView. Parametry, které nemají odpovídající hodnotu v ParameterView, zůstávají nezměněny.
Obecně by váš kód měl při přepisování await base.SetParametersAsync(parameters); volat metodu základní třídy (SetParametersAsync). V pokročilých scénářích může kód vývojáře interpretovat hodnoty přicházejících parametrů jakýmkoli potřebným způsobem, aniž by vyvolal metodu základní třídy. Například není nutné přiřazovat příchozí parametry k vlastnostem třídy. Při vytváření struktury vašeho kódu musíte odkazovat na ComponentBase referenční zdroj, aniž byste volali metodu základní třídy, protože ta volá další metody životního cyklu a spuští vykreslování složitějším způsobem.
Poznámka:
Odkazy na dokumentaci ke zdrojům pro .NET obvykle načítají výchozí větev úložiště, která reprezentuje aktuální vývoj pro další vydání .NET. K výběru značky pro konkrétní vydání použijte rozevírací seznam Přepnout větve nebo značky. Pro více informací si přečtěte Jak vybrat verzi štítku zdrojového kódu ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Pokud se chcete spoléhat na inicializační a vykreslovací logiku ComponentBase.SetParametersAsync, ale nechcete zpracovávat příchozí parametry, máte možnost předat prázdný ParameterView metodě základní třídy.
await base.SetParametersAsync(ParameterView.Empty);
Pokud jsou v kódu vývojáře uvedeny obslužné rutiny událostí, odpojte je při likvidaci. Další informace najdete v tématu odstranění komponenty ASP.NET Core Razor.
V následujícím příkladu ParameterView.TryGetValue přidělí hodnotu parametru Param do value, pokud je úspěšné zpracování parametrů trasy pro Param. Když value není null, hodnota je zobrazena komponentou.
Ačkoli porovnávání parametrů trasy není citlivé na velikost písmen, TryGetValue odpovídá pouze názvům parametrů rozlišujícím velikost písmen v šabloně trasy. Následující příklad vyžaduje použití /{Param?} v šabloně trasy, aby bylo možné získat hodnotu pomocí TryGetValue, nikoli /{param?}. Pokud je /{param?} použit v tomto scénáři, TryGetValue vrátí false a message není nastaven na žádný z řetězců message.
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);
}
}
Inicializace komponenty (OnInitialized{Async})
OnInitialized a OnInitializedAsync se používají výhradně k inicializaci komponenty pro celou dobu životnosti instance komponenty. Hodnoty parametrů a změny hodnot parametrů by neměly ovlivnit inicializaci prováděnou v těchto metodách. Například načítání statických možností do rozevíracího seznamu, který se nemění po celou dobu životnosti komponenty a není závislý na hodnotách parametrů, se provádí v jedné z těchto metod životního cyklu. Pokud hodnoty parametrů nebo změny hodnot parametrů ovlivňují stav komponenty, použijte místo toho OnParametersSet{Async}.
Tyto metody jsou vyvolány, když je komponenta inicializována poté, co obdrží své počáteční parametry ve SetParametersAsync. Synchronní metoda je volána před asynchronní metodou.
Pokud se používá synchronní inicializace nadřazené komponenty, je zaručeno, že inicializace nadřazené komponenty bude dokončena před inicializací podřízené komponenty. Pokud se používá asynchronní inicializace nadřazené komponenty, nelze určit pořadí dokončení inicializace nadřazené a podřízené komponenty, protože závisí na spuštění inicializačního kódu.
Pro synchronní operaci přepište 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}";
}
}
Aby bylo možné provést asynchronní operaci, přepište OnInitializedAsync a použijte operátor await.
protected override async Task OnInitializedAsync()
{
await ...
}
Pokud je použita vlastní bazová třída s vlastní logikou inicializace, zavolejte OnInitializedAsync na bazovou třídu.
protected override async Task OnInitializedAsync()
{
await ...
await base.OnInitializedAsync();
}
Není nutné volat ComponentBase.OnInitializedAsync, pokud není použita vlastní základní třída s vlastní logikou. Pro více informací se podívejte na část Metody životního cyklu základní třídy.
Komponenta musí zajistit, že se nachází ve správném stavu pro vykreslování, když OnInitializedAsync čeká na potenciálně neúplné Task. Pokud metoda vrátí neúplný Task, část metody, která se dokončí synchronně, musí ponechat komponentu ve validním stavu pro vykreslování. Pro více informací si přečtěte úvodní poznámky ke kontextu synchronizace ASP.NET CoreBlazor a likvidaci komponent ASP.NET CoreRazor.
Blazor aplikace, které předem zpracovávají svůj obsah na serveru, volají OnInitializedAsyncdvakrát:
- Jakmile se komponenta zpočátku vykreslí staticky jako součást stránky.
- Podruhé když prohlížeč vykreslí komponentu.
Aby se zabránilo spuštění vývojářského kódu v OnInitializedAsync dvakrát při předvykreslování, přečtěte si sekci Stavové opětovné připojení po předvykreslování. Obsah v této části se zaměřuje na Blazor Web App a stavově orientované SignalRpřipojení. Pokud chcete zachovat stav během provádění inicializačního kódu při předkončování, podívejte se na ASP.NET trvalost stavu jádraBlazor.
Aby se zabránilo spuštění vývojářského kódu v OnInitializedAsync dvakrát při předvykreslování, přečtěte si sekci Stavové opětovné připojení po předvykreslování. Přestože obsah v této sekci se zaměřuje na Blazor Server a stavovou SignalRznovunavázání, scénář pro předrenderování v hostovaných Blazor WebAssembly řešeních (WebAssemblyPrerendered) zahrnuje podobné podmínky a přístupy k zabránění dvojímu spuštění kódu vývojáře. Chcete-li zachovat stav během provádění inicializačního kódu při prerenderingu, podívejte se na téma Integrace komponent ASP.NET Core Razor s MVC nebo Razor Pages.
Během procesu předběžného vykreslování aplikace Blazor nejsou možné určité akce, jako například volání do JavaScriptu (JS interoperabilita). Komponenty mohou potřebovat vykreslit jinak během předběžného vykreslení. Pro více informací nahlédněte do sekce Prerendering with JavaScript interop.
Pokud jsou v kódu vývojáře uvedeny obslužné rutiny událostí, odpojte je při likvidaci. Další informace najdete v tématu odstranění komponenty ASP.NET Core Razor.
Použijte streamované vykreslování se statickým vykreslováním na straně serveru (statické SSR) nebo předběžným vykreslováním pro zlepšení uživatelského zážitku u komponent, které provádějí asynchronní úkoly dlouhodobého charakteru ke kompletnímu vykreslení. Pro více informací, podívejte se na následující zdroje.
- Zpracování neúplných asynchronních akcí při vykreslování (tento článek)
- Vykreslování komponent ASP.NET Core Razor
Po nastavení parametrů (OnParametersSet{Async})
OnParametersSet nebo OnParametersSetAsync se nazývají:
Poté, co je komponenta inicializována v OnInitialized nebo OnInitializedAsync.
Když rodičovská komponenta znovu vykreslí a poskytne:
- Známé nebo primitivní neměnné typy, když se alespoň jeden parametr změnil.
- Parametry komplexního typu. Rámec nemůže vědět, zda se hodnoty parametru s komplexním typem interně změnily, takže rámec vždy považuje sadu parametrů za změněnou, když je přítomen jeden nebo více parametrů s komplexním typem.
Pro více informací o konvencích vykreslování naleznete Vykreslování komponent v ASP.NET CoreRazor.
Synchronní metoda je volána před asynchronní metodou.
Tyto metody lze vyvolat, i když se hodnoty parametrů nezměnily. Toto chování zdůrazňuje potřebu, aby vývojáři implementovali další logiku v metodách, která kontroluje, zda se hodnoty parametrů skutečně změnily před opětovnou inicializací dat nebo stavu závislého na těchto parametrech.
Pro následující příklad komponenty přejděte na stránku komponenty na této URL adrese:
- S počátečním datem, které je přijato
StartDate:/on-parameters-set/2021-03-19 - Bez uvedení počátečního data, kde je hodnotě přiřazena aktuální místní čas:
StartDate
Poznámka:
V trase komponenty není možné současně omezit parametr DateTime pomocí omezení trasy datetime a zvolit parametr jako volitelný. Proto následující komponenta OnParamsSet používá dvě direktivy @page k řízení směrování s dodaným segmentem data v URL i bez něj.
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}).";
}
}
}
Asynchronní práce při aplikaci parametrů a hodnot vlastností musí proběhnout během události životního cyklu OnParametersSetAsync.
protected override async Task OnParametersSetAsync()
{
await ...
}
Pokud je použita vlastní bazová třída s vlastní logikou inicializace, zavolejte OnParametersSetAsync na bazovou třídu.
protected override async Task OnParametersSetAsync()
{
await ...
await base.OnParametersSetAsync();
}
Není nutné volat ComponentBase.OnParametersSetAsync, pokud není použita vlastní základní třída s vlastní logikou. Pro více informací se podívejte na část Metody životního cyklu základní třídy.
Komponenta musí zajistit, že se nachází ve správném stavu pro vykreslování, když OnParametersSetAsync čeká na potenciálně neúplné Task. Pokud metoda vrátí neúplný Task, část metody, která se dokončí synchronně, musí ponechat komponentu ve validním stavu pro vykreslování. Pro více informací si přečtěte úvodní poznámky ke kontextu synchronizace ASP.NET CoreBlazor a likvidaci komponent ASP.NET CoreRazor.
Pokud jsou v kódu vývojáře uvedeny obslužné rutiny událostí, odpojte je při likvidaci. Další informace najdete v tématu odstranění komponenty ASP.NET Core Razor.
Pokud jednorázová komponenta nepoužívá CancellationToken, OnParametersSet a OnParametersSetAsync by měly zkontrolovat, jestli je komponenta zlikvidována. Pokud OnParametersSetAsync vrací neúplné Task, komponenta musí zajistit, aby část metody, která se dokončí synchronně, ponechala komponentu v platném stavu pro vykreslení. Pro více informací si přečtěte úvodní poznámky ke kontextu synchronizace ASP.NET CoreBlazor a likvidaci komponent ASP.NET CoreRazor.
Pro více informací o parametrech a omezeních trasy si přečtěte směrování a navigace v ASP.NET CoreBlazor.
Příklad ruční implementace SetParametersAsync pro zlepšení výkonu v některých scénářích najdete v osvědčených postupech pro vykreslování ASP.NET CoreBlazor.
Po vykreslení komponentu (OnAfterRender{Async})
OnAfterRender a OnAfterRenderAsync jsou vyvolány poté, co se komponenta vykreslí interaktivně a uživatelské rozhraní dokončí aktualizaci (například po přidání prvků do prohlížeče DOM). Odkazy na prvky a komponenty jsou v tomto bodě naplněny. Použijte tuto fázi k provedení dalších kroků inicializace s vykresleným obsahem, jako jsou například interop volání, která interagují s vykreslenými prvky DOM. Synchronní metoda je volána před asynchronní metodou.
Tyto metody nejsou vyvolávány během předběžného vykreslování nebo statického server-side renderingu (statického SSR) na serveru, protože tyto procesy nejsou připojeny k živému prohlížeči DOM a jsou již dokončeny před aktualizací DOM.
Pro OnAfterRenderAsync se komponenta po dokončení libovolného vráceného Task automaticky neregeneruje, aby se zabránilo nekonečné smyčce vykreslování.
OnAfterRender a OnAfterRenderAsync jsou volány poté, co komponenta dokončí vykreslování. Odkazy na prvky a komponenty jsou v tomto bodě naplněny. Použijte tuto fázi k provedení dalších kroků inicializace s vykresleným obsahem, jako jsou například interop volání, která interagují s vykreslenými prvky DOM. Synchronní metoda je volána před asynchronní metodou.
Tyto metody nejsou vyvolávány během předběžného vykreslování, protože předběžné vykreslování není připojeno k živému DOM prohlížeče a je již dokončeno dříve, než je DOM aktualizován.
Pro OnAfterRenderAsync se komponenta po dokončení libovolného vráceného Task automaticky neregeneruje, aby se zabránilo nekonečné smyčce vykreslování.
Parametr firstRender pro OnAfterRender a OnAfterRenderAsync:
- Poprvé je nastaveno na
true, když je instance komponenty vykreslena. - Může být použit k zajištění toho, že inicializační práce se provede pouze jednou.
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");
}
}
Ukázka AfterRender.razor generuje následující výstup na konzoli, když je stránka načtena a je vybráno tlačítko:
OnAfterRender: firstRender = True
HandleClick called
OnAfterRender: firstRender = False
Asynchronní úlohy bezprostředně po vykreslení se musí provádět během události životního cyklu OnAfterRenderAsync.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
}
Pokud je použita vlastní bazová třída s vlastní logikou inicializace, zavolejte OnAfterRenderAsync na bazovou třídu.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
await base.OnAfterRenderAsync(firstRender);
}
Není nutné volat ComponentBase.OnAfterRenderAsync, pokud není použita vlastní základní třída s vlastní logikou. Pro více informací se podívejte na část Metody životního cyklu základní třídy.
I když vrátíte Task z OnAfterRenderAsync, framework nenaplánuje další renderovací cyklus pro vaši komponentu poté, co úloha dokončí. Toto je abyste se vyhnuli nekonečné smyčce vykreslování. To se liší od ostatních metod životního cyklu, které plánují další cyklus vykreslování, jakmile se dokončení vrátí Task.
OnAfterRender a OnAfterRenderAsyncnejsou volány během prerenderovacího procesu na serveru. Metody jsou volány, když je komponenta vykreslena interaktivně po předběžném vykreslení. Když aplikace předběžně vykresluje:
- Komponenta se vykonává na serveru, aby vytvořila statický HTML kód v odpovědi HTTP. Během této fáze nejsou OnAfterRender a OnAfterRenderAsync volány.
- Když se skript Blazor (
blazor.{server|webassembly|web}.js) spustí v prohlížeči, komponenta se znovu spustí v interaktivním režimu vykreslování. Po restartování komponenty jsou OnAfterRender a OnAfterRenderAsyncvolány, protože aplikace již není ve fázi předrenderování.
Pokud jsou v kódu vývojáře uvedeny obslužné rutiny událostí, odpojte je při likvidaci. Další informace najdete v tématu odstranění komponenty ASP.NET Core Razor.
Metody životního cyklu základní třídy
Když přepisujete metody životního cyklu Blazor, není nutné volat metody životního cyklu základní třídy pro ComponentBase. Nicméně, komponenta by měla zavolat přepsanou metodu životního cyklu základní třídy v následujících situacích:
- Při přepisování ComponentBase.SetParametersAsync je
await base.SetParametersAsync(parameters);obvykle vyvolána, protože metoda základní třídy volá jiné metody životního cyklu a spouští rendering složitým způsobem. Pro více informací se podívejte do sekce Když jsou parametry nastaveny (SetParametersAsync). - Pokud metoda základní třídy obsahuje logiku, která musí být provedena. Uživatelé knihovny obvykle volají metody životního cyklu základní třídy při dědění základní třídy, protože základní třídy knihovny často obsahují vlastní logiku životního cyklu k vykonání. Pokud aplikace používá základní třídu z knihovny, podívejte se do dokumentace knihovny pro vedení.
V následujícím příkladu je voláno base.OnInitialized();, aby se zajistilo, že metoda OnInitialized základní třídy bude provedena. Bez zavolání BlazorRocksBase2.OnInitialized se nespustí.
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!");
}
}
Změny stavu (StateHasChanged)
StateHasChanged informuje komponentu, že se její stav změnil. Když je to vhodné, zavolání StateHasChanged zařadí do fronty opětovné vykreslení, které se provede, když je hlavní vlákno aplikace volné.
StateHasChanged je volán automaticky pro metody EventCallback. Další informace o zpětných voláních událostí naleznete v části ASP.NET Core Blazor zpracování událostí.
Pro více informací o vykreslování komponent a o tom, kdy volat StateHasChanged, včetně toho, kdy jej vyvolat pomocí ComponentBase.InvokeAsync, viz vykreslování komponent ASP.NET Core Razor.
Řešte neúplné asynchronní akce při vykreslování
Asynchronní akce prováděné v událostech životního cyklu nemusí být dokončeny před vykreslením komponenty. Objekty mohou být null nebo neúplně naplněné daty, zatímco se provádí metoda životního cyklu. Poskytněte logiku vykreslování pro potvrzení, že objekty jsou inicializovány. Zobrazujte zástupné prvky uživatelského rozhraní (například zprávy o načítání) zatímco objekty jsou null.
V následující komponentě Slow je OnInitializedAsync přepsán, aby asynchronně spustil časově náročnou úlohu. Během doby, kdy isLoading je true, se uživateli zobrazí zpráva o načítání. Po dokončení Task vráceného OnInitializedAsync se komponenta znovu vykreslí s aktualizovaným stavem, který ukazuje zprávu "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);
}
}
Předchozí komponenta používá k zobrazení zprávy načítání proměnnou isLoading. Podobný přístup se používá pro komponentu, která načítá data do kolekce a kontroluje, zda je kolekce null, aby zobrazila zprávu o načítání. Následující příklad zkontroluje kolekci movies pro null, aby zobrazil načítací zprávu nebo zobrazil kolekci filmů:
@if (movies == null)
{
<p><em>Loading...</em></p>
}
else
{
@* display movies *@
}
@code {
private Movies[]? movies;
protected override async Task OnInitializedAsync()
{
movies = await GetMovies();
}
}
Prerendering čeká na klid, což znamená, že komponenta se nevykreslí, dokud všechny komponenty ve stromu vykreslování nedokončí vykreslování. To znamená, že zpráva o načítání se nezobrazuje, když podřízená komponenta metodou OnInitializedAsync spouští dlouhotrvající úlohu při předkreslování. Chcete-li toto chování předvést, umístěte předchozí komponentu Slow do komponenty Home testovací aplikace:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<Slow />
Poznámka:
I když příklady v této části diskutují o metodě životního cyklu OnInitializedAsync, jiné metody životního cyklu, které se provádějí během předkončování, mohou zpozdit konečné vykreslení komponenty. Pouze OnAfterRender{Async} se nespustí během prerenderingu a je imunní vůči zpožděním způsobeným klidem.
Během předkreslování se komponenta Home nevykreslí, dokud se nevykreslí komponenta Slow, což trvá deset sekund. Uživatelské rozhraní je během tohoto desetisekundového období prázdné a není k dispozici žádná zpráva o načítání. Po předběžném vykreslování se vykresluje komponenta Home a zobrazí se zpráva o načítání komponenty Slow. Po deseti sekundách komponenta Slow nakonec zobrazí dokončenou zprávu.
Jak znázorňuje předchozí ukázka, nehybnost během prerenderingu vede k špatné uživatelské zkušenosti. Pro zlepšení uživatelské zkušenosti začněte implementací streamovaného vykreslování, abyste se vyhnuli čekání na dokončení asynchronní úlohy při předběžném vykreslování.
Přidejte atribut [StreamRendering] do komponenty Slow (použijte [StreamRendering(true)] v .NET 8):
@attribute [StreamRendering]
Když je komponenta Home předem vykreslovaná, komponenta Slow se rychle vykresluje se svou zprávou o načítání. Komponenta Home nečeká deset sekund, než komponenta Slow dokončí vykreslování. Dokončená zpráva zobrazená na konci předběžného vykreslování je však nahrazena zprávou načítání, zatímco komponenta nakonec vykresluje, což je další desetisekundové zpoždění. K tomu dochází, protože komponenta Slow se vykresluje dvakrát, stejně jako se LoadDataAsync provádí dvakrát. Když komponenta přistupuje k prostředkům, jako jsou služby a databáze, dvojité spouštění volání služeb a databázových dotazů vytváří nežádoucí zatížení prostředků aplikace.
Pokud chcete vyřešit dvojité vykreslení zprávy o načítání a opětovné spuštění volání služeb a databáze, uchovejte předem vykreslený stav pomocí PersistentComponentState pro konečné vykreslení komponenty, jak je vidět v následujících aktualizacích komponenty Slow.
@page "/slow"
@attribute [StreamRendering]
<h2>Slow Component</h2>
@if (Data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@Data</div>
}
@code {
[PersistentState]
public string? Data { get; set; }
protected override async Task OnInitializedAsync()
{
Data ??= await LoadDataAsync();
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
}
@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()
{
if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
{
data = await LoadDataAsync();
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(data), data);
return Task.CompletedTask;
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
Kombinováním streamování vykreslování se stavem perzistentních komponentů:
- Služby a databáze vyžadují pouze jedno volání inicializace součástí.
- Komponenty rychle vykreslují své uživatelské rozhraní s načítacími zprávami během dlouhotrvajících úloh pro co nejlepší uživatelský zážitek.
Pro více informací, podívejte se na následující zdroje.
- Vykreslování komponent ASP.NET Core Razor
- ASP.NET trvalost stavu jádraBlazor.
:::moniker-end
Nečinnost při předběžném zpracování vede k špatné uživatelské zkušenosti. Zpoždění je možné řešit v aplikacích, které cílí na .NET 8 nebo novější, pomocí funkce nazývané streamované vykreslování, obvykle v kombinaci se zachováním stavu komponent během předevykreslování, aby se zabránilo čekání na dokončení asynchronní úlohy. Ve verzích .NET starších než .NET 8 může provádění dlouhotrvající úlohy na pozadí, která načítá data po konečném vykreslení, řešit zdlouhavé zpoždění vykreslování kvůli klidovému stavu.
Řešit chyby
Pro informace o zpracování chyb během vykonávání životnostních metod viz Zpracování chyb v aplikacích ASP.NET CoreBlazor.
Stavové znovupřipojení po předvykreslení
Při vykreslování na serveru je komponenta zpočátku vykreslena staticky jako součást stránky. Jakmile prohlížeč naváže SignalR spojení zpět se serverem, komponent je znovu zobrazen a interaktivní. Pokud je přítomna metoda OnInitialized{Async} životního cyklu pro inicializaci komponenty, metoda se provede dvakrát:
- Když je komponenta staticky předem vykreslena.
- Po navázání připojení k serveru.
To může vést k znatelné změně v datech zobrazených v uživatelském rozhraní, když je komponenta nakonec vykreslena. Aby se předešlo tomuto chování, zadejte identifikátor, který uloží stav během předběžného vykreslování a získá stav po něm.
Následující kód ukazuje WeatherForecastService, který zabraňuje změně zobrazení dat z důvodu předdekreslování. Očekávaný Delay (await Task.Delay(...)) simuluje krátké zpoždění před vrácením dat z metody GetForecastAsync.
Přidejte služby IMemoryCache s AddMemoryCache do kolekce služeb v souboru aplikace Program.
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();
});
}
}
Pro více informací o RenderMode si prohlédněte Pokyny pro ASP.NET Core BlazorSignalR.
Obsah této sekce se zaměřuje na Blazor Web App a stavové SignalRznovupřipojení. Pokud chcete zachovat stav během provádění inicializačního kódu při předkončování, podívejte se na ASP.NET trvalost stavu jádraBlazor.
Navzdory tomu, že se obsah této sekce zaměřuje na Blazor Server a stavovou SignalRrekonektivitu, scénář pro prerendering v hostovaných Blazor WebAssembly řešeních (WebAssemblyPrerendered) zahrnuje podobné podmínky a přístupy, které mají zabránit dvojímu spuštění vývojářského kódu. Chcete-li zachovat stav během provádění inicializačního kódu při prerenderingu, podívejte se na téma Integrace komponent ASP.NET Core Razor s MVC nebo Razor Pages.
Předběžné vykreslování s interoperabilitou JavaScriptu
Tato sekce se vztahuje na aplikace na straně serveru, které předem vykreslí komponenty Razor. Prerendrování je pokryto v Prerender ASP.NET Core Razor components.
Tato sekce se vztahuje na aplikace na straně serveru, které předem vykreslí komponenty Razor. Prerendrování je pokryto v Prerender ASP.NET Core Razor components.
Poznámka:
Interní navigace pro interaktivní směrování v Blazor Web Apps nezahrnuje žádost o nový obsah stránky ze serveru. Proto se předběžné vykreslení neprovádí při interních požadavcích na stránku. Pokud aplikace přijme interaktivní směrování, proveďte úplné obnovení stránky pro příklady komponent, které demonstrují chování při předběžném vykreslování. Další informace najdete v tématu ASP.NET Blazor trvalost stavu předsekutného jádra.
Tato sekce se vztahuje na aplikace na straně serveru a hostované aplikace Blazor WebAssembly, které předem vykreslují komponenty Razor. Prerendering je zahrnuto v Integrovat komponenty ASP.NET Core Razor s MVC nebo Razor Pages.
Během předvykreslování není možné volat JavaScript (JS). Následující příklad ukazuje, jak použít JS interop jako součást inicializační logiky komponenty způsobem, který je kompatibilní s předrenderováním.
Následující funkce scrollElementIntoView:
- Posune se k předanému prvku pomocí
scrollIntoView. - Vrátí hodnotu vlastnosti
topprvku z metodygetBoundingClientRect.
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Když IJSRuntime.InvokeAsync volá funkci JS v kódu komponenty, ElementReference se používá pouze v OnAfterRenderAsync a nikoli v žádné dřívější metodě životního cyklu, protože není žádný prvek HTML DOM, dokud není komponenta vykreslena.
StateHasChanged (referenční zdroj) je volán k zařazení opětovného vykreslování komponenty s novým stavem získaným z volání JS interop (pro více informací viz vykreslování komponent ASP.NET Core Razor). Nekonečná smyčka není vytvořena, protože StateHasChanged je volána pouze tehdy, když scrollPosition je 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();
}
}
}
Předchozí příklad znečišťuje klienta globální funkcí. Pro lepší přístup v produkčních aplikacích se podívejte na izolaci JavaScriptu v JavaScript modulech.
Práce na pozadí, kterou lze zrušit
Komponenty často provádějí dlouhodobou práci na pozadí, jako je například síťová volání (HttpClient) a interakce s databázemi. Je žádoucí zastavit práci na pozadí, aby se šetřily systémové prostředky v několika situacích. Například asynchronní operace na pozadí se automaticky nezastaví, když se uživatel přesune od komponenty.
Další důvody, proč může být nutné zrušit položky na pozadí, zahrnují:
- Byla spuštěna úloha na pozadí s chybnými vstupními daty nebo parametry zpracování.
- Aktuální sada zpracovávaných úkolů na pozadí musí být nahrazena novou sadou úkolů.
- Priorita aktuálně vykonávaných úkolů musí být změněna.
- Aplikace musí být ukončena kvůli obnovení nasazení serveru.
- Serverové zdroje se stávají omezenými, což vyžaduje přeplánování úloh na pozadí.
K implementaci rušitelného vzoru pro práci na pozadí v komponentě:
- Použijte CancellationTokenSource a CancellationToken.
- Při zneškodnění komponenty a kdykoli je požadováno zrušení, zrušením tokenu ručně, zavolejte
CancellationTokenSource.Cancel, abyste signalizovali, že práce na pozadí by měla být zrušena. - Po vrácení asynchronního volání zavolejte ThrowIfCancellationRequested na tokenu.
V následujícím příkladu:
-
await Task.Delay(10000, cts.Token);představuje dlouhodobou asynchronní úlohu na pozadí. -
BackgroundResourceMethodpředstavuje dlouhodobě běžící metodu na pozadí, která by neměla začínat, pokud jeResourcezlikvidován před voláním této metody.
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;
}
}
}
Pokud chcete zobrazit indikátor načítání, zatímco probíhá práce na pozadí, použijte následující přístup.
Vytvořte komponentu indikátoru načítání s parametrem Loading , který může zobrazit podřízený obsah v souboru RenderFragment.
Loading Pro parametr:
- Když je
true, zobrazte indikátor načítání. - Při
falsevykreslujte obsah komponenty (ChildContent). Další informace naleznete v tématu fragmenty vykreslení podřízeného obsahu.
ContentLoading.razor:
@if (Loading)
{
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public bool Loading { get; set; }
}
Pokud chcete načíst styly CSS pro indikátor, přidejte je do obsahu <head> pomocí komponenty HeadContent. Další informace najdete v tématu Kontrola obsahu hlavičky v aplikacích ASP.NET CoreBlazor.
@if (Loading)
{
<!-- OPTIONAL ...
<HeadContent>
<style>
...
</style>
</HeadContent>
-->
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
...
Zabalte značkování komponenty Razor do komponenty ContentLoading a předejte hodnotu z pole C# do parametru Loading v okamžiku, kdy komponenta provádí inicializační činnost:
<ContentLoading Loading="@loading">
...
</ContentLoading>
@code {
private bool loading = true;
...
protected override async Task OnInitializedAsync()
{
await LongRunningWork().ContinueWith(_ => loading = false);
}
...
}
Blazor Server události znovupřipojení
Události životního cyklu komponent popsané v tomto článku fungují odděleně od zpracovatelů událostí pro opětovné připojení na straně serveru. Když dojde ke ztrátě SignalR připojení ke klientovi, jsou přerušeny pouze aktualizace uživatelského rozhraní. Aktualizace uživatelského rozhraní jsou obnoveny, když je spojení znovu navázáno. Pro více informací o událostech a konfiguraci obslužných rutin obvodů si přečtěte ASP.NET Core BlazorSignalR příručku.
Další zdroje
Řešte zachycené výjimky mimo životní cyklus Razor komponentu