Sdílet prostřednictvím


ASP.NET Core Razor component lifecycle

Note

Toto není nejnovější verze tohoto článku. Pro aktuální verzi si přečtěte článek o verzi .NET 9.

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í vydání si přečtěte verzi tohoto článku pro .NET 9.

Důležité

Tato informace se týká produktu ve fázi předběžného vydání, který může být významně upraven, než bude komerčně uveden na trh. Microsoft neposkytuje žádné záruky, výslovné ani implicitní, ohledně zde poskytnutých informací.

Pro aktuální vydání si přečtěte verzi .NET 9 tohoto článku.

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

  1. Pokud se komponenta vykresluje poprvé na žádost:
    • Vytvořte instanci komponenty.
    • Proveďte vkládání vlastností.
    • Call 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.
  2. Zavolejte OnParametersSet{Async}. Pokud je vrácen neúplný Task, je vyčkáváno na Task a poté je komponent znovu vykreslen. Synchronní metoda je volána před asynchronní metodou.
  3. 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. For more information, see the Handle incomplete asynchronous actions at render section later in this article.

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.

Life cycle události komponenty Razor komponenty v Blazor

DOM event processing:

  1. Obsluha události se spustila.
  2. Pokud je vrácena neúplná Task, vyčká se na Task a poté se komponenta znovu vykreslí.
  3. Provést všechny synchronní úkoly a dokončit Task.

DOM event processing

Životní cyklus Render:

  1. Neprovádějte další operace vykreslování na komponentě, pokud jsou splněny obě následující podmínky:
  2. Sestavte rozdíl mezi stromy vykreslování a vykreslete komponentu.
  3. Počkejte, až se DOM aktualizuje.
  4. Call OnAfterRender{Async}. The synchronous method is called prior to the asynchronous method.

Render lifecycle

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í

In server-side Blazor apps, prerendering waits for quiescence, which means that a component doesn't render until all of the components in the render tree have finished rendering. 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. For more information, see the Handle incomplete asynchronous actions at render section later in this article.

Když jsou nastaveny parametry (SetParametersAsync)

SetParametersAsync sets parameters supplied by the component's parent in the render tree or from route parameters.

The method's ParameterView parameter contains the set of component parameter values for the component each time SetParametersAsync is called. By overriding the SetParametersAsync method, developer code can interact directly with ParameterView's parameters.

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í SetParametersAsync volat metodu základní třídy (await base.SetParametersAsync(parameters);). 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 k referenčnímu zdroji .NET obvykle načítají výchozí větev repozitáře, která představuje aktuální vývoj pro příští vydání .NET. Pro výběr značky pro konkrétní vydání použijte rozevírací seznam Přepnout větve nebo značky. Pro další informace viz Jak vybrat verzi tagu 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. For more information, see ASP.NET Core Razor component disposal.

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 je použita asynchronní inicializace rodičovské komponenty, nelze určit pořadí dokončení inicializace rodičovské a dětské 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 apps that prerender their content on the server call OnInitializedAsynctwice:

  • Once when the component is initially rendered statically as part of the page.
  • A second time when the browser renders the component.

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í. Chcete-li zachovat stav během provádění inicializačního kódu při předběžném vykreslování, podívejte se na Předběžné vykreslování komponent ASP.NET Core Razor.

Aby se při předběžném vykreslování zabránilo opětovnému spuštění kódu vývojáře v OnInitializedAsync, nahlédněte do sekce Připojení státního po opětovném vykreslová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. To preserve state during the execution of initialization code while prerendering, see Integrate ASP.NET Core Razor components with MVC or Razor Pages.

While a Blazor app is prerendering, certain actions, such as calling into JavaScript (JS interop), aren't possible. 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 poskytovány obslužné rutiny událostí, odpojte je při odstranění. For more information, see ASP.NET Core Razor component disposal.

Use streaming rendering with static server-side rendering (static SSR) or prerendering to improve the user experience for components that perform long-running asynchronous tasks in OnInitializedAsync to fully render. Pro více informací, podívejte se na následující zdroje.

Po nastavení parametrů (OnParametersSet{Async})

OnParametersSet or OnParametersSetAsync are called:

  • Poté, co je komponenta inicializována v OnInitialized nebo OnInitializedAsync.

  • When the parent component rerenders and supplies:

    • 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: /on-parameters-set

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í základní třída s vlastní inicializační logikou, volejte OnParametersSetAsync na základní třídě:

protected override async Task OnParametersSetAsync()
{
    await ...

    await base.OnParametersSetAsync();
}

Není nutné volat ComponentBase.OnParametersSetAsync, pokud se nepoužívá vlastní základní třída s vlastní logikou. Pro více informací se podívejte do části Metody životního cyklu základní třídy.

Komponenta musí zajistit, že je ve správném stavu pro vykreslování, když OnParametersSetAsync čeká na potenciálně neúplné Task. Pokud metoda vrátí neúplný Task, ta část metody, která se dokončí synchronně, musí zanechat komponentu ve stavu vhodném pro vykreslování. Pro více informací si přečtěte úvodní poznámky k kontextu synchronizace Blazor ASP.NET Core a likvidaci komponent Razor ASP.NET Core.

Pokud jsou v kódu vývojáře poskytovány obslužné rutiny událostí, odpojte je při likvidaci. For more information, see ASP.NET Core Razor component disposal.

If a disposable component doesn't use a CancellationToken, OnParametersSet and OnParametersSetAsync should check if the component is disposed. 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 k ASP.NET Core Blazor synchronizačnímu kontextu a ASP.NET Core Razor likvidaci komponent.

Pro více informací o parametrech a omezeních trasy si přečtěte směrování a navigace v ASP.NET CoreBlazor.

Pro příklad implementace SetParametersAsync ručně pro zlepšení výkonu v některých scénářích se podívejte na nejlepší postupy výkonu pro 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í. Reference prvků a komponent jsou v tomto bodě zaplněny. Použijte tuto fázi k provedení dalších inicializačních kroků s vykresleným obsahem, například volání JS interop, 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 komponent OnAfterRenderAsync se po dokončení jakéhokoli vráceného Task komponent automaticky nezobrazí, 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.
  • Can be used to ensure that initialization work is only performed once.

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 se používá vlastní základní třída s vlastní logikou inicializace, zavolejte OnAfterRenderAsync na základní třídě.

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ím logickým procesem. For more information, see the Base class lifecycle methods section.

Even if you return a Task from OnAfterRenderAsync, the framework doesn't schedule a further render cycle for your component once that task completes. 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:

  1. 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.
  2. 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í. After a component is restarted, OnAfterRender and OnAfterRenderAsyncare called because the app isn't in the prerendering phase any longer.

Pokud jsou v programátorském kódu k dispozici obslužné rutiny událostí, odpojte je při likvidaci. For more information, see ASP.NET Core Razor component disposal.

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í.

For more information on component rendering and when to call StateHasChanged, including when to invoke it with ComponentBase.InvokeAsync, see ASP.NET Core Razor component rendering.

Ř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.

In the following Slow component, OnInitializedAsync is overridden to asynchronously execute a long-running task. Během doby, kdy isLoading je true, se uživateli zobrazí zpráva o načítání. After the Task returned by OnInitializedAsync completes, the component is rerendered with the updated state, showing the "Finished!" message.

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 waits for quiescence, which means that a component doesn't render until all of the components in the render tree have finished rendering. 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. Only OnAfterRender{Async} isn't executed during prerendering and is immune to delays due to quiescence.

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. To improve the user experience, begin by implementing streaming rendering to avoid waiting for the asynchronous task to complete while prerendering.

Přidejte atribut [StreamRendering] do komponenty Slow (použijte [StreamRendering(true)] v .NET 8):

@attribute [StreamRendering]

When the Home component is prerendering, the Slow component is quickly rendered with its loading message. 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 {
    [SupplyParameterFromPersistentComponentState]
    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();
    }
}

By combining streaming rendering with persistent component state:

  • Služby a databáze vyžadují pouze jedno volání inicializace součástí.
  • Components render their UIs quickly with loading messages during long-running tasks for the best user experience.

Pro více informací si prosím prohlédněte následující zdroje:

:::moniker-end

Nečinnost při předběžném zpracování vede k špatné uživatelské zkušenosti. The delay can be addressed in apps that target .NET 8 or later with a feature called streaming rendering, usually combined with persisting component state during prerendering to avoid waiting for the asynchronous task to complete. Ve verzích rozhraní .NET starších než 8.0 může provedení dlouhotrvající úlohy na pozadí, která načte data po konečném vykreslení, vyřešit dlouhou prodlevu vykreslování způsobenou nečinností.

Handle errors

For information on handling errors during lifecycle method execution, see Handle errors in ASP.NET Core Blazor apps.

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:

  • When the component is prerendered statically.
  • 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.

The following code demonstrates a WeatherForecastService that avoids the change in data display due to prerendering. The awaited Delay (await Task.Delay(...)) simulates a short delay before returning data from the GetForecastAsync method.

Add IMemoryCache services with AddMemoryCache on the service collection in the app's Program file:

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í. Aby byl během vykonávání inicializačního kódu při předrenderování zachován stav, podívejte se na Předrenderování komponent ASP.NET CoreRazor.

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 před vykreslením, podívejte se na Integraci ASP.NET Core Razor komponent s MVC nebo Razor Stránkami.

Předběžné vykreslování s interoperabilitou JavaScriptu

Tato sekce se vztahuje na aplikace na straně serveru, které předem vykreslí komponenty Razor. Prerendering is covered in Prerender ASP.NET Core Razor components.

Note

Internal navigation for interactive routing in Blazor Web Apps doesn't involve requesting new page content from the server. Proto se předběžné vykreslení neprovádí při interních požadavcích na stránku. If the app adopts interactive routing, perform a full page reload for component examples that demonstrate prerendering behavior. Pro více informací se podívejte na Předrenderovat komponenty ASP.NET CoreRazor.

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.

During prerendering, calling into JavaScript (JS) isn't possible. 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:

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.
  • Server resources become limited, necessitating the rescheduling of background work items.

K implementaci rušitelného vzoru pro práci na pozadí v komponentě:

V následujícím příkladu:

  • await Task.Delay(10000, cts.Token); představuje dlouhodobou asynchronní úlohu na pozadí.
  • BackgroundResourceMethod představuje dlouhodobě běžící metodu na pozadí, která by neměla začínat, pokud je Resource zlikvidová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:

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. For more information on circuit handler events and configuration, see ASP.NET Core BlazorSignalR guidance.

Další zdroje

Řešte zachycené výjimky mimo životní cyklus Razor komponentu