Delen via


levenscyclus van ASP.NET Core Razor-onderdelen

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

In dit artikel wordt de levenscyclus van ASP.NET Core Razor-onderdelen uitgelegd en wordt uitgelegd hoe u levenscyclus-gebeurtenissen gebruikt.

Levenscyclus-gebeurtenissen

Het Razor-onderdeel verwerkt levenscyclusgebeurtenissen van het Razor-onderdeel in een set synchrone en asynchrone levenscyclusmethoden. De levenscyclusmethoden kunnen worden overschreven om extra bewerkingen uit te voeren in onderdelen tijdens de initialisatie en rendering van onderdelen.

Dit artikel vereenvoudigt de verwerking van levenscyclusgebeurtenissen voor onderdelen om complexe frameworklogica te verduidelijken en behandelt niet elke wijziging die in de loop van de jaren is aangebracht. Mogelijk moet u toegang krijgen tot de ComponentBase referentiebron om aangepaste gebeurtenisverwerking te integreren met de verwerking van levenscyclus-gebeurtenissen van Blazor. Codeopmerkingen in de referentiebron bevatten aanvullende opmerkingen over de verwerking van levenscyclus-gebeurtenissen die niet worden weergegeven in dit artikel of in de API-documentatie.

Notitie

Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de vervolgkeuzelijst Switch branches or tags. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

De volgende vereenvoudigde diagrammen illustreren de verwerking van levenscyclusgebeurtenissen van Razor-componenten. De C#-methoden die aan de levenscyclus-gebeurtenissen zijn gekoppeld, worden gedefinieerd met voorbeelden in de volgende secties van dit artikel.

Levenscyclus-gebeurtenissen voor onderdelen:

  1. Als het onderdeel voor het eerst wordt weergegeven op een aanvraag:
    • Maak een instantie van de component.
    • Voer eigenschapsinjectie uit.
    • Bel OnInitialized{Async}. Als er een onvolledige Task wordt geretourneerd, wacht men op de Task en wordt de component opnieuw gerenderd. De synchrone methode wordt aangeroepen vóór de asynchrone methode.
  2. Bel OnParametersSet{Async}. Als er een onvolledige Task wordt geretourneerd, wacht men op de Task en wordt de component opnieuw gerenderd. De synchrone methode wordt aangeroepen vóór de asynchrone methode.
  3. Voer alle synchrone werkzaamheden uit en voltooi Tasks.

Notitie

Asynchrone acties die in levenscyclusevenementen worden uitgevoerd, kunnen het weergeven van onderdelen of het weergeven van gegevens vertragen. Voor meer informatie, zie later in dit artikel de sectie over het verwerken van onvolledige asynchrone acties bij renderen in sectie.

Een bovenliggend component wordt weergegeven vóór zijn onderliggende componenten, omdat de rendering bepaalt welke kinderen of elementen aanwezig zijn. Als de initialisatie van synchrone bovenliggende componenten wordt gebruikt, wordt gegarandeerd dat de bovenliggende initialisatie eerst wordt voltooid. Als asynchrone initialisatie van oudercomponenten wordt gebruikt, kan de voltooiingsvolgorde van de initialisatie van ouder- en kindcomponenten niet worden bepaald omdat deze afhankelijk is van de initialisatiecode die wordt uitgevoerd.

levenscyclus van onderdelen van een Razor-onderdeel in Blazor

DOM-gebeurtenisverwerking:

  1. De eventhandler wordt uitgevoerd.
  2. Als er een onvolledige Task wordt geretourneerd, wacht men op de Task en wordt de component opnieuw gerenderd.
  3. Voer alle synchrone werkzaamheden uit en voltooi Tasks.

DOM-gebeurtenisverwerking

De Render levenscyclus:

  1. Vermijd verdere renderingbewerkingen voor het onderdeel wanneer aan beide van de volgende voorwaarden wordt voldaan:
  2. Bouw het verschil tussen de renderstructuur (verschil) en geef het onderdeel weer.
  3. Wacht totdat de DOM wordt bijgewerkt.
  4. Bel OnAfterRender{Async}. De synchrone methode wordt aangeroepen vóór de asynchrone methode.

levenscyclus van renderen

Ontwikkelaars-aanroepen naar StateHasChanged resulteren in een nieuwe aanroep. Zie ASP.NET Core Razor component renderingvoor meer informatie.

Stilstand tijdens prerendering

In server-side Blazor-apps wacht het prerenderen op de toestand van rust van, wat inhoudt dat een component pas wordt weergegeven wanneer alle componenten in de renderstructuur het renderproces hebben voltooid. Quiescentie kan zorgen voor merkbare vertragingen bij het weergeven wanneer een onderdeel langlopende taken uitvoert tijdens de initiatiefase en andere levenscyclusmethoden, wat leidt tot een slechte gebruikerservaring. Voor meer informatie, zie later in dit artikel de sectie over het verwerken van onvolledige asynchrone acties bij renderen in sectie.

Wanneer parameters zijn ingesteld (SetParametersAsync)

SetParametersAsync stelt parameters in die zijn opgegeven door het ouderonderdeel in de renderboom of vanuit routeparameters.

De ParameterView parameter van de methode bevat de set componentparameter waarden voor het onderdeel telkens wanneer SetParametersAsync wordt aangeroepen. Door de methode SetParametersAsync te overschrijven, kan de code van ontwikkelaars rechtstreeks communiceren met de parameters van ParameterView.

Met de standaardimplementatie van SetParametersAsync wordt de waarde van elke eigenschap ingesteld met het [Parameter] of [CascadingParameter] kenmerk dat een bijbehorende waarde heeft in de ParameterView. Parameters die geen overeenkomende waarde in ParameterView hebben, blijven ongewijzigd.

Over het algemeen moet uw code de basisklassemethode (await base.SetParametersAsync(parameters);) aanroepen bij het overschrijven van SetParametersAsync. In geavanceerde scenario's kan code van ontwikkelaars de waarden van de binnenkomende parameters op elke manier interpreteren die vereist zijn door de basisklassemethode niet aan te roepen. Er is bijvoorbeeld geen vereiste om de binnenkomende parameters toe te wijzen aan de eigenschappen van de klasse. U moet echter verwijzen naar de ComponentBase verwijzingsbron bij het structureren van uw code zonder de basisklassemethode aan te roepen, omdat er andere levenscyclusmethoden worden aangeroepen en dat rendering op een complexe manier wordt geactiveerd.

Notitie

Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de vervolgkeuzelijst Switch branches or tags. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

Als u wilt vertrouwen op de initialisatie- en renderinglogica van ComponentBase.SetParametersAsync maar geen binnenkomende parameters verwerkt, hebt u de mogelijkheid om een lege ParameterView door te geven aan de basisklassemethode:

await base.SetParametersAsync(ParameterView.Empty);

Als gebeurtenishandlers worden opgegeven in de code van de ontwikkelaar, moet u deze loskoppelen bij verwijdering. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

In het volgende voorbeeld wijst ParameterView.TryGetValue de waarde van de Param parameter toe aan value als het parseren van een routeparameter voor Param is geslaagd. Wanneer value niet is null, wordt de waarde weergegeven door het onderdeel.

Hoewel routeparameterkoppeling niet hoofdlettergevoelig is, komt TryGetValue alleen overeen met hoofdlettergevoelige parameternamen in de routesjabloon. In het volgende voorbeeld is het gebruik van /{Param?} in de routesjabloon vereist om de waarde met TryGetValueop te halen, niet /{param?}. Als /{param?} in dit scenario wordt gebruikt, retourneert TryGetValuefalse en is message niet ingesteld op message tekenreeks.

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);
    }
}

Initialisatie van onderdelen (OnInitialized{Async})

OnInitialized en OnInitializedAsync worden uitsluitend gebruikt om een component voor de gehele levensduur van de componentinstance te initialiseren. Parameterwaarden en parameterwaardewijzigingen mogen geen invloed hebben op de initialisatie die in deze methoden wordt uitgevoerd. Als u bijvoorbeeld statische opties laadt in een vervolgkeuzelijst die niet verandert gedurende de levensduur van de component en die niet afhankelijk is van parameterwaarden, wordt dit uitgevoerd in een van deze levenscyclusmethoden. Als parameterwaarden of wijzigingen in parameterwaarden van invloed zijn op de onderdeelstatus, gebruikt u in plaats daarvan OnParametersSet{Async}.

Deze methoden worden aangeroepen wanneer het onderdeel wordt geïnitialiseerd nadat de initiële parameters in SetParametersAsynczijn ontvangen. De synchrone methode wordt aangeroepen vóór de asynchrone methode.

Als synchrone initialisatie van bovenliggende componenten wordt gebruikt, is het gegarandeerd dat de bovenliggende initialisatie is voltooid voordat de initialisatie van de onderliggende componenten wordt voltooid. Als asynchrone initialisatie van oudercomponenten wordt gebruikt, kan de voltooiingsvolgorde van de initialisatie van ouder- en kindcomponenten niet worden bepaald omdat deze afhankelijk is van de initialisatiecode die wordt uitgevoerd.

Voor een synchrone bewerking, kunt u OnInitializedoverschrijven.

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}";
    }
}

Als u een asynchrone bewerking wilt uitvoeren, overschrijft u OnInitializedAsync en gebruikt u de operator await:

protected override async Task OnInitializedAsync()
{
    await ...
}

Als een aangepaste basisklasse wordt gebruikt met aangepaste initialisatielogica, roept u OnInitializedAsync aan op de basisklasse:

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

    await base.OnInitializedAsync();
}

Het is niet nodig om ComponentBase.OnInitializedAsync aan te roepen, tenzij een aangepaste basisklasse wordt gebruikt met aangepaste logica. Zie de sectie Levenscyclusmethoden voor basisklassen voor meer informatie.

Een onderdeel moet ervoor zorgen dat het een geldige status heeft voor rendering wanneer OnInitializedAsync wordt gewacht op een mogelijk onvolledige Task. Als de methode een onvolledige Taskretourneert, moet het deel van de methode dat synchroon wordt voltooid, het onderdeel in een geldige status laten staan voor rendering. Zie voor meer informatie de inleidende opmerkingen van ASP.NET Core Blazor synchronisatiecontext en ASP.NET Core Razor component verwijdering.

Blazor apps die hun inhoud vooraf op de server renderen, oproepen OnInitializedAsynctwee keer:

  • Eenmaal wanneer het onderdeel in eerste instantie statisch wordt weergegeven als onderdeel van de pagina.
  • Een tweede keer wanneer de browser het onderdeel weergeeft.

Als u wilt voorkomen dat ontwikkelaarscode in OnInitializedAsync twee keer wordt uitgevoerd tijdens de voorweergave, raadpleegt u de sectie Statusvolle verbinding na voorafgaande weergave. De inhoud in de sectie is gericht op Blazor Web Apps en stateful SignalRopnieuw verbinding maken. Zie Prerender ASP.NET Core Razor componentenom tijdens het prerenderen de status te behouden bij het uitvoeren van initialisatiecode.

Als u wilt voorkomen dat ontwikkelaarscode in OnInitializedAsync twee keer wordt uitgevoerd tijdens de voorweergave, raadpleegt u de sectie Statusvolle verbinding na voorafgaande weergave. Hoewel de inhoud van de sectie gericht is op Blazor Server en stateful SignalR-herverbinding, omvat het scenario voor prerendering van gehoste Blazor WebAssembly-oplossingen (WebAssemblyPrerendered) vergelijkbare voorwaarden en benaderingen om te voorkomen dat de ontwikkelaarscode twee keer wordt uitgevoerd. Om de status te behouden tijdens het uitvoeren van initialisatiecode, zie Integreer ASP.NET Core Razor-onderdelen met MVC of Razor Pages.

Hoewel een Blazor-app wordt voorbereid, zijn bepaalde acties, zoals het aanroepen van JavaScript (JS interop), niet mogelijk. Onderdelen moeten mogelijk anders worden weergegeven wanneer ze vooraf worden weergegeven. Zie de sectie Prerendering with JavaScript interop voor meer informatie.

Als gebeurtenishandlers worden opgegeven in de code van de ontwikkelaar, moet u deze loskoppelen bij verwijdering. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

Gebruik streaming rendering met statische server-side rendering (SSR) of prerendering om de gebruikerservaring te verbeteren voor componenten die langlopende asynchrone taken uitvoeren om volledig te renderen in OnInitializedAsync. Zie de volgende bronnen voor meer informatie:

Nadat parameters zijn ingesteld (OnParametersSet{Async})

OnParametersSet of OnParametersSetAsync worden aangeroepen:

  • Nadat het onderdeel is geïnitialiseerd in OnInitialized of OnInitializedAsync.

  • Wanneer de bovenliggende component opnieuw rendert en levert:

    • Bekende of primitieve onveranderbare typen wanneer ten minste één parameter is gewijzigd.
    • Complexe getypeerde parameters. Het framework kan niet weten of de waarden van een complex getypte parameter intern zijn gemuteerd, dus behandelt het framework de parameterset altijd als gewijzigd wanneer een of meer complex getypte parameters aanwezig zijn.

    Zie ASP.NET Core Razor component renderingvoor meer informatie over renderingconventies.

De synchrone methode wordt aangeroepen vóór de asynchrone methode.

De methoden kunnen worden aangeroepen, zelfs als de parameterwaarden niet zijn gewijzigd. Dit gedrag onderstreept de noodzaak voor ontwikkelaars om extra logica binnen de methoden te implementeren om te controleren of parameterwaarden inderdaad zijn gewijzigd voordat gegevens of status opnieuw worden geïnitialiseerd, afhankelijk van deze parameters.

Voor het volgende voorbeeldonderdeel gaat u naar de pagina van het onderdeel op een URL:

  • Met een begindatum die door StartDatewordt ontvangen: /on-parameters-set/2021-03-19
  • Zonder een begindatum, waarbij aan StartDate een waarde van de huidige lokale tijd wordt toegewezen: /on-parameters-set

Notitie

In een onderdeelroute is het niet mogelijk om een DateTime parameter te beperken met de routebeperking datetime en de parameter optioneel te maken. Daarom gebruikt het volgende OnParamsSet onderdeel twee @page instructies voor het afhandelen van routering met en zonder een opgegeven datumsegment in de URL.

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}).";
        }
    }
}

Asynchroon werk bij het toepassen van parameters en eigenschapswaarden moet plaatsvinden tijdens de OnParametersSetAsync levenscyclus gebeurtenis:

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

Als een aangepaste basisklasse wordt gebruikt met aangepaste initialisatielogica, roept u OnParametersSetAsync aan op de basisklasse:

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

    await base.OnParametersSetAsync();
}

Het is niet nodig om ComponentBase.OnParametersSetAsync aan te roepen, tenzij een aangepaste basisklasse wordt gebruikt met aangepaste logica. Zie de sectie Levenscyclusmethoden voor basisklassen voor meer informatie.

Een onderdeel moet ervoor zorgen dat het een geldige status heeft voor rendering wanneer OnParametersSetAsync wordt gewacht op een mogelijk onvolledige Task. Als de methode een onvolledige Taskretourneert, moet het deel van de methode dat synchroon wordt voltooid, het onderdeel in een geldige status laten staan voor rendering. Zie voor meer informatie de inleidende opmerkingen van ASP.NET Core Blazor synchronisatiecontext en ASP.NET Core Razor component verwijdering.

Als gebeurtenishandlers worden opgegeven in de code van de ontwikkelaar, moet u deze loskoppelen bij verwijdering. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

Als een wegwerponderdeel geen CancellationTokengebruikt, moet OnParametersSet en OnParametersSetAsync controleren of het onderdeel is verwijderd. Als OnParametersSetAsync een onvolledige Taskretourneert, moet de component ervoor zorgen dat het gedeelte van de methode dat synchroon wordt voltooid, de component in een geldige staat voor renderen achterlaat. Zie voor meer informatie de inleidende opmerkingen van ASP.NET Core Blazor synchronisatiecontext en ASP.NET Core Razor component verwijdering.

Zie ASP.NET Core Blazor routering en navigatievoor meer informatie over routeparameters en beperkingen.

Zie SetParametersAsync voor een voorbeeld van het handmatig implementeren van Blazor om de prestaties in sommige scenario's te verbeteren.

Na het renderen van onderdelen (OnAfterRender{Async})

OnAfterRender en OnAfterRenderAsync worden aangeroepen nadat een onderdeel interactief is weergegeven en de gebruikersinterface is bijgewerkt (bijvoorbeeld nadat elementen zijn toegevoegd aan de browser-DOM). Element- en onderdeelverwijzingen worden op dit moment ingevuld. Gebruik deze fase om aanvullende initialisatiestappen uit te voeren met de weergegeven inhoud, zoals JS interop-aanroepen die communiceren met de weergegeven DOM-elementen. De synchrone methode wordt aangeroepen vóór de asynchrone methode.

Deze methoden worden niet aangeroepen tijdens prerendering of statische server-side rendering (statische SSR) op de server, omdat deze processen niet zijn gekoppeld aan een live browser-DOM en al zijn voltooid voordat het DOM wordt bijgewerkt.

Voor OnAfterRenderAsyncwordt het onderdeel niet automatisch her-gerendert nadat alle geretourneerde Task zijn voltooid om een oneindige render-cyclus te voorkomen.

OnAfterRender en OnAfterRenderAsync worden aangeroepen nadat een onderdeel klaar is met renderen. Element- en onderdeelverwijzingen worden op dit moment ingevuld. Gebruik deze fase om aanvullende initialisatiestappen uit te voeren met de weergegeven inhoud, zoals JS interop-aanroepen die communiceren met de weergegeven DOM-elementen. De synchrone methode wordt aangeroepen vóór de asynchrone methode.

Deze methoden worden niet aangeroepen tijdens het prerendering, omdat prerendering niet is gekoppeld aan een live browser DOM en al is voltooid voordat de DOM wordt bijgewerkt.

Voor OnAfterRenderAsyncwordt het onderdeel niet automatisch her-gerendert nadat alle geretourneerde Task zijn voltooid om een oneindige render-cyclus te voorkomen.

De parameter firstRender voor OnAfterRender en OnAfterRenderAsync:

  • Wordt ingesteld op true de eerste keer dat de onderdeelinstantie wordt weergegeven.
  • Kan worden gebruikt om ervoor te zorgen dat initialisatiewerkzaamheden slechts eenmaal worden uitgevoerd.

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");
    }
}

Het AfterRender.razor voorbeeld produceert de volgende uitvoer naar de console wanneer de pagina wordt geladen en de knop is geselecteerd:

OnAfterRender: firstRender = True
HandleClick called
OnAfterRender: firstRender = False

Asynchroon werk moet direct na rendering plaatsvinden tijdens de OnAfterRenderAsync levenscyclusfase.

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    ...
}

Als een aangepaste basisklasse wordt gebruikt met aangepaste initialisatielogica, roept u OnAfterRenderAsync aan op de basisklasse:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    ...

    await base.OnAfterRenderAsync(firstRender);
}

Het is niet nodig om ComponentBase.OnAfterRenderAsync aan te roepen, tenzij een aangepaste basisklasse wordt gebruikt met aangepaste logica. Zie de sectie Levenscyclusmethoden voor basisklassen voor meer informatie.

Zelfs als u een Task van OnAfterRenderAsyncretourneert, plant het framework geen verdere rendercyclus voor uw onderdeel zodra deze taak is voltooid. Dit is om een oneindige renderlus te voorkomen. Dit verschilt van de andere levenscyclusmethoden, die een extra rendercyclus plannen zodra een geretourneerde Task is afgerond.

OnAfterRender en OnAfterRenderAsyncworden niet aangeroepen tijdens het prerenderingsproces op de server. De methoden worden aangeroepen wanneer het onderdeel interactief wordt weergegeven na het prerendering. Wanneer de app voorgerenderd wordt:

  1. Het onderdeel wordt uitgevoerd op de server om een aantal statische HTML-markeringen te produceren in het HTTP-antwoord. Tijdens deze fase worden OnAfterRender en OnAfterRenderAsync niet aangeroepen.
  2. Wanneer het Blazor script (blazor.{server|webassembly|web}.js) in de browser wordt gestart, wordt het onderdeel opnieuw gestart in een interactieve renderingmodus. Nadat een onderdeel opnieuw is opgestart, worden OnAfterRender en OnAfterRenderAsync aangeroepen omdat de app zich niet meer in de prerenderingsfase bevindt.

Als gebeurtenishandlers worden opgegeven in de code van de ontwikkelaar, moet u deze loskoppelen bij verwijdering. Zie ASP.NET Core Razor component verwijderingvoor meer informatie.

Levenscyclusmethoden van de basisklasse

Wanneer u de levenscyclusmethoden van Blazoroverschrijft, is het niet nodig om levenscyclusmethoden voor basisklassen aan te roepen voor ComponentBase. Een onderdeel moet echter een levenscyclusmethode van een basisklasse die is overschreven aanroepen in de volgende situaties:

  • Bij het overschrijven van ComponentBase.SetParametersAsyncwordt await base.SetParametersAsync(parameters); meestal aangeroepen omdat de basisklassemethode andere levenscyclusmethoden aanroept en op een complexe manier rendering triggert. Zie de sectie Wanneer parameters zijn ingesteld (SetParametersAsync) voor meer informatie.
  • Als de basisklassemethode logica bevat die moet worden uitgevoerd. Bibliotheekgebruikers roepen meestal levenscyclusmethoden van basisklassen aan bij het overnemen van een basisklasse, omdat bibliotheekbasisklassen vaak aangepaste levenscycluslogica hebben om uit te voeren. Als de app een basisklasse uit een bibliotheek gebruikt, raadpleegt u de documentatie van de bibliotheek voor hulp.

In het volgende voorbeeld wordt base.OnInitialized(); aangeroepen om ervoor te zorgen dat de OnInitialized methode van de basisklasse wordt uitgevoerd. Zonder de aanroep wordt BlazorRocksBase2.OnInitialized niet uitgevoerd.

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!");
    }
}

Statuswijzigingen (StateHasChanged)

StateHasChanged meldt het onderdeel dat de status is gewijzigd. Indien van toepassing, roept het aanroepen van StateHasChanged een hertekening aan die optreedt wanneer de hoofdthread van de app vrij is.

StateHasChanged wordt automatisch aangeroepen voor EventCallback methoden. Zie ASP.NET Core Blazor event handlingvoor meer informatie over callbacks van gebeurtenissen.

Zie StateHasChangedvoor meer informatie over het weergeven van onderdelen en wanneer u ComponentBase.InvokeAsyncaanroept, inclusief wanneer u deze aanroept met Razor.

Verwerk onvolledige asynchrone acties tijdens het renderen

Asynchrone acties die in levenscyclusgebeurtenissen worden uitgevoerd, zijn mogelijk niet voltooid voordat het onderdeel wordt weergegeven. Objecten kunnen null zijn of onvolledig gevuld met gegevens wanneer de levenscyclusmethode wordt uitgevoerd. Geef weergavelogica op om te controleren of objecten zijn geïnitialiseerd. Tijdelijke UI-elementen weergeven (bijvoorbeeld een laadbericht) terwijl objecten nullzijn.

In het volgende Slow onderdeel wordt OnInitializedAsync overschreven om asynchroon een langlopende taak uit te voeren. Terwijl isLoadingtrueis, wordt er een berichtje voor het laden aan de gebruiker weergegeven. Nadat de Task, geretourneerd door OnInitializedAsync, is voltooid, wordt het onderdeel opnieuw weergegeven met de bijgewerkte status, en wordt het bericht 'Finished!' getoond.

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);
    }
}

Het voorgaande onderdeel maakt gebruik van een isLoading variabele om het laadbericht weer te geven. Een vergelijkbare benadering wordt gebruikt voor een onderdeel dat gegevens in een verzameling laadt en controleert of de verzameling is null om het laadbericht te presenteren. In het volgende voorbeeld wordt de movies-collectie gecontroleerd op null om een laadbericht weer te geven of de filmcollectie te tonen.

@if (movies == null)
{
    <p><em>Loading...</em></p>
}
else
{
    @* display movies *@
}

@code {
    private Movies[]? movies;

    protected override async Task OnInitializedAsync()
    {
        movies = await GetMovies();
    }
}

Prerendering wacht op rusttoestand, wat betekent dat een component niet wordt gerenderd totdat alle componenten in de renderboom klaar zijn met renderen. Dit betekent dat een laadbericht niet wordt weergegeven terwijl de OnInitializedAsync methode van een onderliggend onderdeel een langlopende taak uitvoert tijdens het voorbereiden. Als u dit gedrag wilt demonstreren, plaatst u het voorgaande Slow onderdeel in het Home-onderdeel van een test-app:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<Slow />

Notitie

Hoewel in de voorbeelden in deze sectie de OnInitializedAsync levenscyclusmethode wordt besproken, kunnen andere levenscyclusmethoden die worden uitgevoerd tijdens het prerenderen de uiteindelijke rendering van een onderdeel vertragen. Alleen OnAfterRender{Async} wordt niet uitgevoerd tijdens prerendering en is immuun voor vertragingen als gevolg van stilte.

Tijdens het vooraf renderen wordt het Home-onderdeel pas weergegeven zodra het Slow-onderdeel is weergegeven, wat tien seconden duurt. De gebruikersinterface is leeg tijdens deze periode van tien seconden en er is geen laadbericht. Na het vooraf renderen wordt het Home-component weergegeven, en het laadbericht van het Slow-component wordt getoond. Na tien seconden wordt het voltooide bericht weergegeven in het Slow onderdeel.

Zoals in de voorgaande demonstratie wordt geïllustreerd, leidt quiescentie tijdens het prerenderen tot een slechte gebruikerservaring. Als u de gebruikerservaring wilt verbeteren, begint u met het implementeren van streaming-rendering om te voorkomen dat u moet wachten totdat de asynchrone taak is voltooid tijdens het prerenderen.

Voeg het kenmerk [StreamRendering] toe aan het Slow-onderdeel (gebruik [StreamRendering(true)] in .NET 8):

@attribute [StreamRendering]

Wanneer de Home-component aan het prerenderen is, wordt de Slow-component snel weergegeven met zijn laadbericht. Het Home-onderdeel wacht niet tien seconden totdat het Slow onderdeel de rendering heeft voltooid. Het voltooide bericht dat aan het einde van de prerendering wordt weergegeven, wordt echter vervangen door het laadbericht terwijl het onderdeel eindelijk wordt weergegeven, wat nog tien seconden vertraging is. Dit komt doordat het Slow-onderdeel twee keer wordt weergegeven en dat LoadDataAsync twee keer wordt uitgevoerd. Wanneer een onderdeel toegang heeft tot resources, zoals services en databases, maakt dubbele uitvoering van service-aanroepen en databasequery's ongewenste belasting voor de resources van de app.

Als u de dubbele rendering van het laadbericht en de heruitvoering van service- en database-aanroepen wilt aanpakken, behoudt u de vooraf gegenereerde status met PersistentComponentState voor de definitieve rendering van het onderdeel, zoals te zien is in de volgende updates van het Slow-onderdeel:

@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();
    }
}

Door streaming-rendering te combineren met de status van een permanent onderdeel:

  • Services en databases vereisen slechts één aanroep voor de initialisatie van onderdelen.
  • Onderdelen geven hun gebruikersinterfaces snel weer met laadberichten tijdens langlopende taken voor de beste gebruikerservaring.

Zie de volgende bronnen voor meer informatie:

:::moniker-end

Quiescentie tijdens de voorweergave resulteert in een slechte gebruikerservaring. De vertraging kan worden opgelost in apps die .NET 8 of hoger targetten met een functie met de naam streaming rendering, gecombineerd met het behouden van componentstatus tijdens prerendering om te voorkomen dat er moet worden gewacht tot de asynchrone taak is voltooid. In versies van .NET ouder dan .NET 8 kan het uitvoeren van een langlopende achtergrondtaak waarmee de gegevens na de uiteindelijke rendering worden geladen, een lange renderingvertraging oplossen vanwege de rusttijd.

Fouten verwerken

Zie Fouten afhandelen in ASP.NET Core Blazor-appsvoor informatie over het afhandelen van fouten tijdens het uitvoeren van de levenscyclusmethode.

Stateful opnieuw verbinding maken na vooraf rendering

Wanneer er vooraf gerenderd wordt op de server, wordt een component initieel statisch weergegeven als onderdeel van de pagina. Zodra de browser een SignalR-verbinding met de server tot stand brengt, wordt de component opnieuw weergegeven en interactief. Als de OnInitialized{Async} levenscyclusmethode voor het initialiseren van het onderdeel aanwezig is, wordt de methode tweemaal uitgevoerd :

  • Wanneer het onderdeel statisch vooraf wordt gerenderd.
  • Nadat de serververbinding tot stand is gebracht.

Dit kan leiden tot een merkbare wijziging in de gegevens die worden weergegeven in de gebruikersinterface wanneer het onderdeel eindelijk wordt weergegeven. Om dit gedrag te voorkomen, geeft u een id door om de status tijdens het prerenderen in de cache op te slaan en om de status op te halen na het prerenderen.

De volgende code demonstreert een WeatherForecastService die de wijziging in de gegevensweergave vermijdt door vooraf renderen. De verwachte Delay (await Task.Delay(...)) simuleert een korte vertraging voordat gegevens worden teruggegeven van de methode GetForecastAsync.

Voeg IMemoryCache services toe met AddMemoryCache aan de service collectie in het Program bestand van de app.

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();
        });
    }
}

Voor meer informatie over de RenderMode, zie Blazor.

De inhoud in deze sectie is gericht op Blazor Web Appen stateful SignalRopnieuw verbinding maken. Zie Prerender ASP.NET Core Razor componentenom tijdens het prerenderen de status te behouden bij het uitvoeren van initialisatiecode.

Hoewel de inhoud in deze sectie zich richt op Blazor Server en stateful SignalRherverbinding, omvat het scenario voor het vooraf genereren van gehoste Blazor WebAssembly-oplossingen (WebAssemblyPrerendered) vergelijkbare voorwaarden en benaderingen om te voorkomen dat ontwikkelaarscode twee keer wordt uitgevoerd. Om de status te behouden tijdens het uitvoeren van initialisatiecode, zie Integreer ASP.NET Core Razor-onderdelen met MVC of Razor Pages.

Prerendering met JavaScript-interoperabiliteit

Deze sectie is van toepassing op apps aan de serverzijde die Razor componenten vooraf renderen. Prerendering wordt behandeld in Prerender ASP.NET Core Razor onderdelen.

Notitie

Interne navigatie voor interactieve routering in Blazor Web Appomvat niet het aanvragen van nieuwe pagina-inhoud van de server. Daarom vindt het prerendering niet plaats voor interne paginaaanvragen. Als de app gebruikmaakt van interactieve routering, herlaad de volledige pagina voor componentvoorbeelden die het prerenderingsgedrag demonstreren. Zie Prerender ASP.NET Core Razor-onderdelenvoor meer informatie.

Deze sectie is van toepassing op server-side apps en gehoste Blazor WebAssembly-apps die Razor componenten vooraf renderen. Prerendering wordt behandeld in Integratie van ASP.NET Core Razor-componenten met MVC of Razor-pagina's.

Tijdens het prerenderen is het aanroepen van JavaScript (JS) niet mogelijk. In het volgende voorbeeld ziet u hoe u JS interop kunt gebruiken als onderdeel van de initialisatielogica van een component op een manier die compatibel is met prerendering.

De volgende scrollElementIntoView functie:

window.scrollElementIntoView = (element) => {
  element.scrollIntoView();
  return element.getBoundingClientRect().top;
}

Wanneer IJSRuntime.InvokeAsync de functie JS aanroept in onderdeelcode, wordt de ElementReference alleen gebruikt in OnAfterRenderAsync en niet in een eerdere levenscyclusmethode omdat er geen HTML DOM-element is totdat het onderdeel is weergegeven.

StateHasChanged (verwijzingsbron) wordt aangeroepen om de rerendering van het onderdeel in de wachtrij te plaatsen met de nieuwe status die is verkregen uit de JS interop-aanroep (zie ASP.NET Core Razor component rendering). Er wordt geen oneindige lus gemaakt omdat StateHasChanged alleen wordt aangeroepen wanneer scrollPosition wordt 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();
        }
    }
}

Het voorgaande voorbeeld vervuilt de klant met een globale functie. Zie JavaScript-isolatie in JavaScript-modulesvoor een betere benadering van productie-apps.

Annuleerbaar achtergrondwerk

Onderdelen voeren vaak langlopende achtergrondwerkzaamheden uit, zoals het maken van netwerkoproepen (HttpClient) en het werken met databases. Het is wenselijk om het achtergrondwerk te stoppen om systeembronnen in verschillende situaties te besparen. Achtergrondbewerkingen met asynchrone bewerkingen worden bijvoorbeeld niet automatisch gestopt wanneer een gebruiker weg navigeert van een onderdeel.

Andere redenen waarom achtergrondwerkitems annulering vereisen, zijn onder andere:

  • Er is een achtergrondtaak uitgevoerd met onjuiste invoergegevens of verwerkingsparameters.
  • De huidige set van het uitvoeren van achtergrondwerkitems moet worden vervangen door een nieuwe set werkitems.
  • De prioriteit van het uitvoeren van taken moet worden gewijzigd.
  • De app moet worden afgesloten voor het opnieuw implementeren van de server.
  • Serverresources worden beperkt, waardoor het opnieuw plannen van achtergrondwerkitems noodzakelijk is.

Een annuleerbaar achtergrondwerkpatroon in een onderdeel implementeren:

In het volgende voorbeeld:

  • await Task.Delay(10000, cts.Token); vertegenwoordigt langlopend asynchroon achtergrondwerk.
  • BackgroundResourceMethod vertegenwoordigt een langlopende achtergrondmethode die niet moet worden gestart als de Resource wordt verwijderd voordat de methode wordt aangeroepen.

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;
        }
    }
}

Als u een laadindicator wilt weergeven terwijl het achtergrondwerk plaatsvindt, gebruikt u de volgende benadering.

Maak een laadindicator onderdeel met een Loading parameter die inhoud voor kinderen in een RenderFragment kan weergeven. Voor de Loading parameter:

  • Wanneer true, een laadindicator weergeven.
  • Wanneer false, geeft u de inhoud van het onderdeel weer (ChildContent). Zie Renderfragmenten voor kindinhoud voor meer informatie.

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; }
}

Als u CSS-stijlen voor de indicator wilt laden, voegt u de stijlen toe aan <head> inhoud met het HeadContent onderdeel. Zie Hoofdinhoud beheren in ASP.NET Core-apps Blazorvoor meer informatie.

@if (Loading)
{
    <!-- OPTIONAL ...
    <HeadContent>
        <style>
            ...
        </style>
    </HeadContent>
    -->
    <progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
    @ChildContent
}

...

Wikkel de Razor markup van de component met de ContentLoading component en geef een waarde door in een C# veld aan de Loading parameter wanneer het component het initialisatiewerk uitvoert.

<ContentLoading Loading="@loading">
    ...
</ContentLoading>

@code {
    private bool loading = true;
    ...

    protected override async Task OnInitializedAsync()
    {
        await LongRunningWork().ContinueWith(_ => loading = false);
    }

    ...
}

gebeurtenissen voor herverbinding Blazor Server

De onderdelenlevenscyclusgebeurtenissen die in dit artikel worden behandeld, werken onafhankelijk van gebeurtenis-afhandelingsprogramma’s aan de serverzijde. Wanneer de SignalR verbinding met de client is verbroken, worden alleen UI-updates onderbroken. Ui-updates worden hervat wanneer de verbinding opnieuw tot stand is gebracht. Zie ASP.NET Core BlazorSignalR guidancevoor meer informatie over circuit-handler-gebeurtenissen en -configuraties.

Aanvullende informatiebronnen

Afhandelen van onderschepte uitzonderingen buiten de levenscyclus van een Razor onderdeel