Delen via


ASP.NET Core Razor componentweergave

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 uitgelegd hoe Razor-componentweergave in ASP.NET Core Blazor-apps werkt, inclusief wanneer je StateHasChanged moet aanroepen om handmatig een onderdeel weer te geven.

Renderingconventies voor ComponentBase

Componenten moeten worden weergegeven wanneer ze voor het eerst worden toegevoegd aan de componentenhiërarchie door een bovenliggende component. Dit is de enige keer dat een onderdeel moet worden weergegeven. Onderdelen kunnen op andere momenten weergeven volgens hun eigen logica en conventies.

Razor onderdelen overnemen van de ComponentBase basisklasse, die logica bevat voor het activeren van rerendering op de volgende momenten:

Onderdelen die zijn overgenomen van ComponentBase rerenders overslaan vanwege parameterupdates als een van de volgende waarden waar is:

  • Alle parameters zijn afkomstig van een set bekende typen† of een primitief type dat niet is gewijzigd sinds de vorige set parameters is ingesteld.

    †De Blazor framework maakt gebruik van een set ingebouwde regels en expliciete parametertypecontroles voor wijzigingsdetectie. Deze regels en typen kunnen op elk gewenst moment worden gewijzigd. Zie de ChangeDetection-API in de ASP.NET Core-referentiebronvoor meer informatie.

    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 Switch branches or tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.

  • De overschrijving van de methode ShouldRender van het onderdeel retourneert false (de standaard ComponentBase-implementatie retourneert altijd true).

De renderingstroom beheren

In de meeste gevallen resulteren ComponentBase-conventies in de juiste subset van componentherrenderingen na een gebeurtenis. Ontwikkelaars hoeven meestal geen handmatige logica te bieden om het framework te vertellen welke onderdelen opnieuw moeten worden gebruikt en wanneer ze opnieuw moeten worden gebruikt. Het algemene effect van de conventies van het framework is dat het onderdeel dat een gebeurtenis ontvangt zichzelf rerendert. Dit triggert vervolgens recursief het rerenderen van onderliggende onderdelen waarvan de parameterwaarden mogelijk zijn gewijzigd.

Zie ASP.NET Core Blazor prestatiegerichte best practicesvoor meer informatie over de gevolgen van de conventies van het framework en hoe u de componentenhiërarchie van een app optimaliseert voor rendering.

Streaming-rendering

Gebruik streaming rendering met statische server-side rendering (statische SSR) of prerendering om inhoudsupdates te streamen en de gebruikerservaring te verbeteren voor componenten die langlopende asynchrone taken uitvoeren zodat ze volledig kunnen renderen.

Denk bijvoorbeeld aan een onderdeel dat een langlopende databasequery of web-API-aanroep maakt om gegevens weer te geven wanneer de pagina wordt geladen. Normaal gesproken moeten asynchrone taken die worden uitgevoerd als onderdeel van het weergeven van een onderdeel aan de serverzijde worden voltooid voordat het gerenderde antwoord wordt verzonden, waardoor het laden van de pagina kan worden vertraagd. Elke aanzienlijke vertraging bij het weergeven van de pagina heeft gevolgen voor de gebruikerservaring. Om de gebruikerservaring te verbeteren, wordt met streaming-rendering de hele pagina in eerste instantie snel weergegeven met tijdelijke aanduidingen voor inhoud terwijl asynchrone bewerkingen worden uitgevoerd. Nadat de bewerkingen zijn voltooid, wordt de bijgewerkte inhoud verzonden naar de client op dezelfde antwoordverbinding en gepatcht in de DOM.

Streaming-rendering vereist dat de server de uitvoer niet buffert. De antwoordgegevens moeten naar de client stromen wanneer de gegevens worden gegenereerd. Voor hosts die buffering afdwingen, verslechtert streaming-rendering probleemloos en wordt de pagina geladen zonder streaming-rendering.

Als u inhoudsupdates wilt streamen wanneer u statische SSR of prerendering gebruikt, past u het kenmerk [StreamRendering] toe in .NET 9 of hoger (gebruik [StreamRendering(true)] in .NET 8) op de component. Streamingrendering moet expliciet worden ingeschakeld omdat gestreamde updates ertoe kunnen leiden dat inhoud op de pagina wordt verplaatst. Onderdelen zonder het kenmerk gebruiken automatisch streaming-rendering als het bovenliggende onderdeel gebruikmaakt van de functie. Geef false door aan het kenmerk in een onderliggend onderdeel om de functie op dat moment uit te schakelen en verderop in de substructuur van het onderdeel. Het kenmerk is functioneel wanneer het wordt toegepast op componenten die worden geleverd door een Razor classbibliotheek.

Het volgende voorbeeld is gebaseerd op het Weather-onderdeel in een app die is gemaakt op basis van de Blazor Web App projectsjabloon. De aanroep van Task.Delay simuleert het asynchroon ophalen van weergegevens. Het onderdeel geeft in eerste instantie tijdelijke aanduidingen weer ("Loading...") zonder te wachten tot de asynchrone vertraging is voltooid. Wanneer de asynchrone vertraging is voltooid en de weergegevensinhoud is gegenereerd, wordt de inhoud gestreamd naar het antwoord en geïntegreerd in de weersvoorspellingstabel.

Weather.razor:

@page "/weather"
@attribute [StreamRendering]

...

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        ...
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    ...

    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        await Task.Delay(500);

        ...

        forecasts = ...
    }
}

Vernieuwen van gebruikersinterface onderdrukken (ShouldRender)

ShouldRender wordt aangeroepen telkens wanneer een onderdeel wordt weergegeven. Overschrijf ShouldRender om het vernieuwen van de gebruikersinterface te beheren. Als de implementatie trueretourneert, wordt de gebruikersinterface vernieuwd.

Zelfs als ShouldRender wordt overschreven, wordt het onderdeel altijd in eerste instantie weergegeven.

ControlRender.razor:

@page "/control-render"

<PageTitle>Control Render</PageTitle>

<h1>Control Render Example</h1>

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender() => shouldRender;

    private void IncrementCount() => currentCount++;
}
@page "/control-render"

<PageTitle>Control Render</PageTitle>

<h1>Control Render Example</h1>

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender() => shouldRender;

    private void IncrementCount() => currentCount++;
}
@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/control-render"

<label>
    <input type="checkbox" @bind="shouldRender" />
    Should Render?
</label>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;
    private bool shouldRender = true;

    protected override bool ShouldRender()
    {
        return shouldRender;
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Zie ASP.NET Core Blazor best practices voor prestatiesvoor meer informatie over best practices voor prestaties die betrekking hebben op ShouldRender.

StateHasChanged

Wanneer de hoofdthread van de app beschikbaar is, plaatst het aanroepen van StateHasChanged een her-rendering in de wachtrij.

Onderdelen worden in de wachtrij geplaatst voor rendering en worden niet opnieuw in de wachtrij geplaatst als er al een rerender in behandeling is. Als een onderdeel StateHasChanged vijf keer achter elkaar in een lus aanroept, wordt het onderdeel slechts één keer weergegeven. Dit gedrag is gecodeerd in ComponentBase, dat eerst controleert of het een hernieuwde weergave in de wachtrij heeft geplaatst voordat het er nog een toevoegt.

Een component kan meerdere keren tijdens dezelfde cyclus worden gerenderd, wat meestal gebeurt wanneer een component kinderen heeft die met elkaar communiceren.

  • Een component rendert verschillende kindcomponenten.
  • Onderliggende subcomponenten renderen en activeren een update op de oudercomponent.
  • Een bovenliggend onderdeel wordt opnieuw gerenderd met een nieuwe status.

Met dit ontwerp kan StateHasChanged indien nodig worden aangeroepen zonder het risico op onnodige rendering. U kunt altijd deze controle in individuele componenten overnemen door IComponent rechtstreeks te implementeren en handmatig te bepalen wanneer de component wordt gerenderd.

Houd rekening met de volgende IncrementCount methode waarmee een telling wordt verhoogd, StateHasChangedwordt aangeroepen en het aantal opnieuw wordt verhoogd:

private void IncrementCount()
{
    currentCount++;
    StateHasChanged();
    currentCount++;
}

Als u de code in het foutopsporingsprogramma doorloopt, denkt u misschien dat het aantal updates in de gebruikersinterface voor de eerste currentCount++ uitvoering direct nadat StateHasChanged is aangeroepen. In de gebruikersinterface wordt echter geen bijgewerkt aantal weergegeven omdat de synchrone verwerking plaatsvindt voor de uitvoering van deze methode. Er is geen mogelijkheid voor de renderer om het onderdeel weer te geven totdat de gebeurtenis-handler is voltooid. De gebruikersinterface geeft verhogingen weer voor zowel currentCount++-uitvoeringen als in één render.

Als u iets verwacht tussen de currentCount++ lijnen, geeft de verwachte aanroep de renderer de kans om weer te geven. Dit heeft ertoe geleid dat sommige ontwikkelaars Delay aanroepen met een vertraging van één milliseconde binnen hun componenten om een render te laten plaatsvinden, maar we raden u niet aan om een app willekeurig te vertragen om een render in de wachtrij te plaatsen.

De beste aanpak is om te wachten op Task.Yield, waardoor de component code asynchroon verwerkt en tijdens de huidige batch weergeeft met een tweede render in een afzonderlijke batch nadat de opgeleverde taak de voortzetting uitvoert.

Houd rekening met de volgende herziene IncrementCount-methode, die de gebruikersinterface twee keer bijwerkt omdat de rendering die door StateHasChanged in de wachtrij is geplaatst, wordt uitgevoerd wanneer de taak wordt onderbroken met de aanroep van Task.Yield.

private async Task IncrementCount()
{
    currentCount++;
    StateHasChanged();
    await Task.Yield();
    currentCount++;
}

Wees voorzichtig om StateHasChanged niet onnodig aan te roepen. Dit is een veelvoorkomende fout die onnodige renderingkosten oplegt. Code hoeft geen StateHasChanged aan te roepen wanneer:

  • Het routinematig verwerken van gebeurtenissen, zowel synchroon als asynchroon, aangezien ComponentBase een rendering activeert voor de meeste routine-gebeurtenishandlers.
  • Het implementeren van typische levenscycluslogica, zoals OnInitialized of OnParametersSetAsync, ongeacht of dit synchroon of asynchroon is, omdat ComponentBase een render activeert voor typische levenscyclus-gebeurtenissen.

Het kan echter zinvol zijn om StateHasChanged aan te roepen in de gevallen die worden beschreven in de volgende secties van dit artikel:

Een asynchrone handler omvat meerdere asynchrone fasen

Vanwege de manier waarop taken worden gedefinieerd in .NET, kan een ontvanger van een Task alleen de uiteindelijke voltooiing observeren, niet tussenliggende asynchrone statussen. Daarom kan ComponentBase alleen rerendering activeren wanneer de Task voor het eerst wordt geretourneerd en wanneer de Task uiteindelijk is voltooid. Het framework kan niet weten om een onderdeel opnieuw te laden op andere tussenliggende punten, zoals wanneer een IAsyncEnumerable<T>gegevens retourneert in een reeks tussenliggende Task. Als u wilt terugkeren op tussenliggende punten, roept u StateHasChanged aan op die punten.

Houd rekening met het volgende CounterState1-onderdeel, dat het aantal vier keer bijwerkt telkens wanneer de methode IncrementCount wordt uitgevoerd:

  • Automatische renders worden uitgevoerd na de eerste en laatste verhogingen van currentCount.
  • Handmatige renders worden geactiveerd door aanroepen naar StateHasChanged wanneer het framework niet automatisch rerenders activeert op tussenliggende verwerkingspunten waar currentCount wordt verhoogd.

CounterState1.razor:

@page "/counter-state-1"

<PageTitle>Counter State 1</PageTitle>

<h1>Counter State Example 1</h1>

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}
@page "/counter-state-1"

<PageTitle>Counter State 1</PageTitle>

<h1>Counter State Example 1</h1>

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}
@page "/counter-state-1"

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}
@page "/counter-state-1"

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}
@page "/counter-state-1"

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}
@page "/counter-state-1"

<p>
    Current count: @currentCount
</p>

<p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</p>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {
        currentCount++;
        // Renders here automatically

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        StateHasChanged();

        await Task.Delay(1000);
        currentCount++;
        // Renders here automatically
    }
}

Een oproep ontvangen van iets buiten het Blazor rendering- en gebeurtenisafhandelingssysteem

ComponentBase is alleen bekend met zijn eigen levenscyclusmethoden en door Blazorgeactiveerde gebeurtenissen. ComponentBase weet niet over andere gebeurtenissen die zich in code kunnen voordoen. "Bijvoorbeeld, C#-gebeurtenissen die door een aangepast gegevensarchief worden getriggerd, zijn niet bekend bij Blazor." Als u wilt dat dergelijke gebeurtenissen rerendering activeren om bijgewerkte waarden weer te geven in de gebruikersinterface, roept u StateHasChangedaan.

Houd rekening met het volgende CounterState2 onderdeel dat gebruikmaakt van System.Timers.Timer om een telling regelmatig bij te werken en roept StateHasChanged aan om de gebruikersinterface bij te werken:

Omdat de callback buiten de synchronisatiecontext van Blazorwordt aangeroepen, moet het onderdeel de logica van OnTimerCallback in ComponentBase.InvokeAsync verpakken om deze naar de synchronisatiecontext van de renderer te verplaatsen. Dit komt overeen met het verplaatsen naar de gebruikersinterface-thread in andere UI-frameworks. StateHasChanged kan alleen worden aangeroepen vanuit de synchronisatiecontext van de renderer en genereert anders een uitzondering:

System.InvalidOperationException: 'De huidige thread is niet gekoppeld aan de dispatcher. Gebruik InvokeAsync() om de uitvoering over te schakelen naar de Dispatcher bij het activeren van rendering of onderdeelstatus.'

CounterState2.razor:

@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<PageTitle>Counter State 2</PageTitle>

<h1>Counter State Example 2</h1>

<p>
    This counter demonstrates <code>Timer</code> disposal.
</p>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<PageTitle>Counter State 2</PageTitle>

<h1>Counter State Example 2</h1>

<p>
    This counter demonstrates <code>Timer</code> disposal.
</p>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}
@page "/counter-state-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>
    Current count: @currentCount
</p>

@code {
    private int currentCount = 0;
    private Timer timer = new Timer(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

Een onderdeel weergeven buiten de substructuur die wordt gegenereerd door een bepaalde gebeurtenis

De gebruikersinterface kan betrekking hebben op:

  1. Een gebeurtenis verzenden naar één onderdeel.
  2. Een bepaalde status wijzigen.
  3. Rerendering een volledig ander onderdeel dat geen afstammeling is van het onderdeel dat de gebeurtenis ontvangt.

Een manier om met dit scenario om te gaan, is door een statusbeheer te bieden klasse, vaak als een afhankelijkheidsinjectieservice (DI) die in meerdere onderdelen is geïnjecteerd. Wanneer één onderdeel een methode aanroept voor de statusbeheerder, genereert de statusbeheerder een C#-gebeurtenis die vervolgens wordt ontvangen door een onafhankelijk onderdeel.

Zie de volgende resources voor benaderingen voor het beheren van de status:

Voor de state manager-benadering bevinden C#-gebeurtenissen zich buiten de Blazor renderingpijplijn. Roep StateHasChanged aan voor andere onderdelen die u opnieuw wilt gebruiken als reactie op de gebeurtenissen van de statusmanager.

De state manager-benadering is vergelijkbaar met het eerdere geval met System.Timers.Timer in de vorige sectie. Omdat de uitvoeringsaanroepstack doorgaans in de synchronisatiecontext van de renderer blijft staan, is het aanroepen van InvokeAsync normaal gesproken niet vereist. Het aanroepen van InvokeAsync is alleen vereist als de logica de synchronisatiecontext escapet, zoals het aanroepen van ContinueWith op een Task of wachten op een Task met ConfigureAwait(false). Zie voor meer informatie de sectie Een oproep ontvangen van iets buiten het Blazor rendering- en gebeurtenisafhandelingssysteem.

Voortgangsindicator voor het laden van WebAssembly voor Blazor Web Apps

Een voortgangsindicator voor laden is niet aanwezig in een app die is gemaakt op basis van de Blazor Web App projectsjabloon. Er is een nieuwe voortgangsindicator voor het laden gepland voor een toekomstige release van .NET. Ondertussen kan een app aangepaste code gebruiken om een voortgangsindicator voor laden te maken. Zie ASP.NET Core Blazor startupvoor meer informatie.