Megosztás a következőn keresztül:


ASP.NET Core Razor-összetevők renderelése

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadást lásd a .NET 9-es verzióban ebben a cikkben .

Ez a cikk ismerteti Razor összetevő renderelését ASP.NET Core Blazor-alkalmazásokban, beleértve azt is, hogy mikor kell meghívni StateHasChanged, hogy manuálisan aktiváljon egy összetevőt a rendereléshez.

Megjelenítési konvenciók ComponentBase

A összetevőknek renderelniük kell, amikor egy szülőösszetevő először hozzáadja azokat az összetevő-hierarchiához. Ez az egyetlen alkalom, amikor egy összetevőnek renderelnie kell. Az összetevők máskor renderelhetők saját logikájuk és konvencióik szerint.

Razor összetevők az ComponentBase alaposztálytól öröklődnek, amely a következő időpontokban történő újrarendezés aktiválásához szükséges logikát tartalmazza:

Ha az alábbiak valamelyike igaz, az ComponentBase-ból örökölt összetevők kihagyják az újrarendereléseket a paraméterfrissítések miatt.

  • Az összes paraméter ismert típusok készletéből származik† vagy bármely primitív típusú, amely az előző paraméterkészlet beállítása óta nem változott.

    †A Blazor keretrendszer beépített szabályok és explicit paramétertípus-ellenőrzések készletét használja a változásészleléshez. Ezek a szabályok és a típusok bármikor változhatnak. További információt a ASP.NET Core referenciaforrás API-jában talál.

    Jegyzet

    A .NET referenciaforrásra mutató dokumentációs hivatkozások általában betöltik az adattár alapértelmezett ágát, amely a .NET következő kiadásának aktuális fejlesztését jelöli. Egy adott kiadás címkéjének kiválasztásához használja a Címkék vagy ágak váltása legördülő listát. További információ: A ASP.NET Core-forráskód (dotnet/AspNetCore.Docs #26205) verziócímkéjének kiválasztása.

  • Az összetevő ShouldRender metódusának felülbírálásafalse ad vissza (az alapértelmezett ComponentBase implementáció mindig truead vissza).

A renderelési folyamat szabályozása

A legtöbb esetben ComponentBase konvenciók az összetevő-rerenderek megfelelő részhalmazát eredményezik egy esemény bekövetkezése után. A fejlesztőknek általában nem kell manuális logikát megadniuk ahhoz, hogy megadják a keretrendszernek, hogy mely összetevők legyenek újrarendezve, és mikor kell újrarendezniük őket. A keretrendszer konvencióinak általános hatása az, hogy az esemény-rerendereket fogadó összetevő rekurzív módon aktiválja azoknak a leszármazott összetevőknek az újrarendezését, amelyek paraméterértékei módosulhattak.

A keretrendszer konvencióinak teljesítménykövetkezményeivel és az alkalmazás összetevőhierarchiájának renderelésre való optimalizálásával kapcsolatos további információkért tekintse meg az ASP.NET Core Blazor renderelési teljesítményével kapcsolatos ajánlott eljárásokat.

Folyamatos renderelés

A stream alapú renderelést a statikus kiszolgálóoldali rendereléssel (statikus SSR) vagy előrendereléssel, a válaszfolyamon történő tartalomfrissítések streamelésére használhatja, hogy javítsa a felhasználói élményt azon összetevők esetében, amelyek hosszú ideig futó aszinkron feladatokat hajtanak végre, míg a teljes renderelési folyamat befejeződik.

Vegyük például azt az összetevőt, amely hosszú ideig futó adatbázis-lekérdezést vagy webes API-hívást indít az adatok renderelésére az oldal betöltésekor. A kiszolgálóoldali összetevők renderelése során végrehajtott aszinkron feladatoknak általában a renderelt válasz elküldése előtt kell befejeződniük, ami késleltetheti a lap betöltését. Az oldal megjelenítésének jelentős késleltetése árt a felhasználói élménynek. A felhasználói élmény javítása érdekében a folyamatos renderelés kezdetben gyorsan megjeleníti a teljes oldalt helyőrző tartalommal, miközben aszinkron műveletek futnak. A műveletek befejezése után a rendszer elküldi a frissített tartalmat az ügyfélnek ugyanazon a válaszkapcsolaton, és a DOM-ba lesz javítva.

A streamelési rendereléshez a kiszolgálónak el kell kerülnie a kimenet pufferelését. A válaszadatoknak az adatok létrehozásakor át kell áramlani az ügyfélnek. Azoknál a gazdagépeknél, amelyek kényszerítik a pufferelést, a streamelési megjelenítés zökkenőmentesen múlik el, és az oldal betöltődik a streamelési megjelenítés nélkül.

Ha a tartalomfrissítéseket statikus kiszolgálóoldali renderelés (statikus SSR) vagy előrendelés használatakor szeretné streamelni, alkalmazza a [StreamRendering] attribútumot a .NET 9-ben vagy újabb verzióiban (a .NET 8-ban használja a [StreamRendering(true)]) az összetevőre. A streamelési renderelést explicit módon engedélyezni kell, mert a streamelt frissítések miatt előfordulhat, hogy a lap tartalma eltolódik. Az attribútum nélküli összetevők automatikusan streamelési renderelést vezetnek be, ha a szülőösszetevő használja a funkciót. Adja át a false attribútumot egy gyermekösszetevőnek, hogy letiltsa a funkciót ezen a ponton és az összetevői struktúrában is továbbre haladva. Az attribútum akkor működik, ha egy Razor osztálykódtáráltal biztosított összetevőkre alkalmazza.

Ha a továbbfejlesztett navigáció aktív, a streamelési renderelés nem található válaszokat jelenít meg a lap újrabetöltése nélkül. Ha a továbbfejlesztett navigáció le van tiltva, a keretrendszer egy oldalfrissítéssel átirányítja a Nem található tartalomra.

A streamelési renderelés csak olyan összetevőket képes megjeleníteni, amelyek útvonallal rendelkeznek, például egy NotFoundPage hozzárendelés (NotFoundPage="...") vagy egy Állapotkódlapok újrafuttatásának middleware-hozzárendelése (UseStatusCodePagesWithReExecute). A Nem található renderelési töredék (<NotFound>...</NotFound>) és a DefaultNotFound 404-es tartalom ("Not foundegyszerű szöveg") nem rendelkezik útvonalokkal, ezért nem használhatók a streamelési renderelés során.

Streamelt NavigationManager.NotFound tartalom renderelése (sorrendben):

  • Az NotFoundPage komponensnek átadott Router, ha van ilyen.
  • Ha konfigurálva van, egy állapotkód-oldalakat újrafuttató köztes szoftver oldala.
  • Nincs teendő, ha az előző megközelítések egyikét sem alkalmazzák.

A nem streamelt NavigationManager.NotFound tartalom renderelése (sorrendben):

  • Az NotFoundPage komponensnek átadott Router, ha van ilyen.
  • Nem található renderelt töredék tartalom, ha elérhető. A .NET 10-ben vagy újabb verziókban nem ajánlott.
  • DefaultNotFound 404 tartalom ("Not found" egyszerű szöveg).

Az állapotkódlapok újrafuttatását végző köztes szoftver elsőbbséget élvez a böngészőalapú címútválasztási problémáknál, például a böngésző címsorába beírt helytelen URL-címnél, vagy olyan hivatkozás kiválasztásakor, amelynek nincs végpontja az alkalmazásban.UseStatusCodePagesWithReExecute

Az alábbi példa a Weatherlétrehozott alkalmazás Blazor Web App összetevőjén alapul. A Task.Delay hívása az időjárási adatok aszinkron beolvasását szimulálja. Az összetevő kezdetben a helyőrző tartalmat ("Loading...") jeleníti meg anélkül, hogy megvárná az aszinkron késleltetési idő végét. Amikor az aszinkron késés befejeződik, és az időjárási adatok létrejönnek, a tartalom a válaszba kerül streamelésre, majd bekerül az időjárás-előrejelzési táblába.

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 = ...
    }
}

Felhasználói felület frissítésének letiltása (ShouldRender)

ShouldRender minden egyes összetevő renderelésekor meghívjuk. Felülbírálja a(z) ShouldRender a felhasználói felület frissítésének kezelésére. Ha a megvalósítás truead vissza, a felhasználói felület frissül.

Még akkor is, ha ShouldRender felülbírálva van, az összetevő eredetileg mindig megjelenik.

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

További információ a teljesítményre vonatkozó ajánlott eljárásokról ShouldRender: ASP.NET Core Blazor renderelési teljesítményre vonatkozó ajánlott eljárások.

StateHasChanged

A StateHasChanged hívása egy rerendert hoz létre, amely akkor fordul elő, ha az alkalmazás fő szála ingyenes.

Az összetevőket sorba állítják a rendereléshez, és nem kerülnek újra sorba állításra, ha már van függőben lévő újrarenderelés. Ha egy összetevő egymás után ötször hív meg StateHasChanged egy ciklusban, az összetevő csak egyszer jelenik meg. Ez a viselkedés a ComponentBasekódolva van, amely először ellenőrzi, hogy várólistára helyezett-e egy újrarendelőt, mielőtt újabbat hoz létre.

Az összetevők több alkalommal is renderelhetők ugyanabban a ciklusban, ami általában akkor fordul elő, ha egy összetevő gyermekekkel kommunikál egymással:

  • A szülőösszetevő több gyermeket jelenít meg.
  • A gyermekösszetevők megjelenítést végeznek és frissítést kezdeményeznek a szülőn.
  • A szülőkomponens újrarajzolja magát új állapottal.

Ez a kialakítás lehetővé teszi StateHasChanged meghívását, ha szükséges, anélkül, hogy szükségtelen renderelést kellene bevezetnie. E viselkedés felett az egyes összetevőkben mindig átveheti az irányítást, ha közvetlenül IComponent-t implementálja, és manuálisan kezeli az összetevő renderelését.

Fontolja meg a következő IncrementCount metódust, amely növeli a darabszámot, meghívja StateHasChanged, és ismét növeli a darabszámot:

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

Ha végiglép a hibakeresőben lévő kódon, azt gondolhatja, hogy a számláló az első currentCount++ végrehajtás után azonnal frissül a felhasználói felületen, miután a StateHasChanged-et meghívják. A felhasználói felület azonban ezen a ponton nem jelenít meg frissített darabszámot, mivel a metódus végrehajtásához szinkron feldolgozás zajlik. A renderelőnek nincs lehetősége az összetevő renderelésére, amíg az eseménykezelő be nem fejeződik. A felhasználói felület mindkét currentCount++ végrehajtások növekedéseit egy renderelés során jeleníti meg.

Ha a currentCount++ sorok között várakozik valamire, az érkező hívás lehetőséget teremt a renderelő számára a megjelenítésre. Ez azt eredményezte, hogy egyes fejlesztők egy ezredmásodperces késleltetéssel hívják meg az Delay-t az összetevőkben, hogy lehetővé tegyék a renderelést, de nem javasoljuk az alkalmazás indokolatlan lassítását annak érdekében, hogy sorba állítsuk a renderelést.

A legjobb megoldás a Task.Yield-ra várakozni, amely arra kényszeríti az összetevőt, hogy aszinkron dolgozza fel a kódot, és renderelje az aktuális csomagban egy második rendereléssel egy külön csomagban, miután a feladat befejezte a folytatást.

Fontolja meg a következő módosított IncrementCount metódust, amely kétszer frissíti a felhasználói felületet, mert a StateHasChanged által lekért renderelés akkor történik, amikor a feladat a Task.Yieldhívásával jön létre:

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

Ügyeljen arra, hogy ne hívja fel szükségtelenül StateHasChanged, ami gyakori hiba, amely szükségtelen költségekkel jár. A kódnak nem kell meghívnia StateHasChanged, amikor:

  • Események rutinszerű kezelése, akár szinkron, akár aszinkron módon, mivel ComponentBase a legtöbb rutinesemény-kezelőhöz renderelést aktivál.
  • A tipikus életciklus-logika, például OnInitialized vagy OnParametersSetAsyncimplementálása szinkronban vagy aszinkron módon is, mivel ComponentBase renderelést indít el a tipikus életciklus-eseményekhez.

A cikk következő szakaszaiban ismertetett esetekben azonban érdemes lehet meghívni StateHasChanged:

Az aszinkron folyamatkezelő több aszinkron fázist tartalmaz

A feladatok .NET-ben való definiálásának köszönhetően egy Task fogadója csak a végleges befejezést figyelheti meg, köztes aszinkron állapotokat nem. Ezért ComponentBase csak a Task első visszaadásakor és a Task befejeződésekor aktiválhatja az újrarendezést. A keretrendszer nem tudja más közbenső pontokon újrarajzolni összetevőt, például amikor egy IAsyncEnumerable<T>adatokat ad vissza közbenső Task-kban. Ha köztes pontokon szeretne újrarendezni, hívja StateHasChanged ezeken a pontokon.

Vegye figyelembe a következő CounterState1 összetevőt, amely a IncrementCount metódus minden végrehajtásakor négyszer frissíti a darabszámot:

  • Az automatikus renderelés a currentCountelső és utolsó növekménye után történik.
  • A manuális rendereléseket a StateHasChanged hívásai aktiválják, amikor a keretrendszer nem indít el automatikusan újrarendereléseket olyan köztes feldolgozási pontokon, ahol a currentCount értéke megnövekszik.

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

Hívás fogadása a Blazor renderelési és eseménykezelő rendszer külső részéről

ComponentBase csak saját életciklus-módszereiről és Blazoráltal aktivált eseményekről tud. ComponentBase nem tud a kódban előforduló egyéb eseményekről. A C#-események, amelyeket egy egyéni adattár vált ki, például ismeretlenek Blazorszámára. Annak érdekében, hogy az ilyen események újrarendezést válthassanak ki a frissített értékek felhasználói felületen való megjelenítéséhez, hívja meg a StateHasChanged.

Vegye figyelembe a következő CounterState2 összetevőt, amely System.Timers.Timer használ a szám rendszeres időközönkénti frissítéséhez, és meghívja StateHasChanged a felhasználói felület frissítésére:

  • OnTimerCallback bármely Blazorfelügyelt renderelési folyamaton vagy eseményértesítésen kívül fut. Ezért a(z) OnTimerCallback-nak/-nek hívnia kell a StateHasChanged-t, mert a Blazor nincs tisztában a visszahívás alatt a currentCount módosításaival.
  • Az összetevő megvalósítja a IDisposable-t, amelyben a Timer el van távolítva, amikor a keretrendszer meghívja a Dispose metódust. További információért lásd: ASP.NET Core Razor összetevők eltávolítása.

Mivel a visszahívás Blazorszinkronizálási környezetén kívül történik, az összetevőnek be kell csomagolnia a OnTimerCallback logikáját ComponentBase.InvokeAsync a renderelő szinkronizálási környezetébe való áthelyezéséhez. Ez azzal egyenértékű, hogy a felhasználói felületi szálra történő átváltást végzünk más felhasználói felületi keretrendszerekben. StateHasChanged csak a renderelő szinkronizálási környezetéből hívható meg, és kivételt jelez másként:

System.InvalidOperationException: "Az aktuális szál nincs társítva a diszpécserrel. Az InvokeAsync() használatával váltsa át a végrehajtást a Dispatcherre, amikor a renderelést vagy az összetevő állapotát kiváltja.

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

Egy adott esemény által újrarendezett részhalmazon kívüli összetevő megjelenítése

A felhasználói felület a következőket vonhatja maga után:

  1. Esemény elküldése egy összetevőnek.
  2. Állapot megváltoztatása.
  3. Teljesen más összetevő újrarendezése, amely nem az eseményt fogadó összetevő leszármazottja.

Ennek a forgatókönyvnek az egyik módja, hogy biztosítsunk egy állapotkezelési osztályt, amelyet gyakran függőséginjektálási (DI) szolgáltatásként használnak, és több összetevőbe injektálnak. Amikor egy összetevő metódust hív meg az állapotkezelőn, az állapotkezelő létrehoz egy C#-eseményt, amelyet aztán egy független összetevő fogad.

Az állapot kezelésének megközelítéseit az alábbi forrásokban találhatja meg:

Az állapotkezelői megközelítés esetében a C#-események a Blazor renderelési folyamaton kívülre kerülnek. Azokat az összetevőket hívja meg StateHasChanged, amelyeket az állapotkezelő eseményeire válaszul újra szeretne renderelni.

Az állapotkezelő megközelítés hasonló a System.Timers.Timerelőző szakaszban található korábbi esethez. Mivel a végrehajtási hívás verem általában a renderelő szinkronizálási környezetében marad, a InvokeAsync hívása általában nem szükséges. A InvokeAsync meghívása csak akkor szükséges, ha a logika kikerül a szinkronizálási környezetből, például meghívja az ContinueWith-et egy Task-re, és várja a Task-at a ConfigureAwait(false)-gyel. További információkért lásd a Hívás fogadása a Blazor renderelési és eseménykezelő rendszeren kívül című szakaszt.

WebAssembly betöltési folyamatjelző Blazor Web Appmásodperc számára

Az alkalmazások egyéni kódot hozhatnak létre a betöltési folyamatjelző létrehozásához. További információ: ASP.NET Core Blazor indítási.