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


ASP.NET Core Razor-összetevők életciklusa

Megjegyzés

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 cikk .NET 9-es verziójában.

Ez a cikk az ASP.NET Core Razor összetevő életciklusát és az életciklus-események használatát ismerteti.

Életciklus-események

A Razor összetevő szinkron és aszinkron életciklus-metódusok halmazában dolgozza fel Razor összetevők életciklus-eseményeit. Az életciklus-metódusokat felül lehet bírálni az összetevők további műveleteinek végrehajtásához az összetevők inicializálása és renderelése során.

Ez a cikk leegyszerűsíti az összetevők életciklusának eseményfeldolgozását az összetett keretrendszerlogika tisztázása érdekében, és nem fedi le az évek során végrehajtott összes módosítást. Előfordulhat, hogy hozzá kell férnie a ComponentBase referenciaforráshoz, hogy integrálja az egyéni eseményfeldolgozást Blazoréletciklus-eseményfeldolgozással. A referenciaforrás kód megjegyzései további megjegyzéseket tartalmaznak az életciklus-eseményfeldolgozással kapcsolatban, amelyek nem jelennek meg ebben a cikkben vagy a API dokumentációjában.

Megjegyzés

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 az Ágak vagy címkék közötti váltás 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 alábbi egyszerűsített diagramok Razor összetevők életciklusának eseményfeldolgozását szemléltetik. Az életciklus-eseményekhez társított C#-metódusok a cikk következő szakaszaiban található példákkal vannak definiálva.

Összetevő életciklus-eseményei:

  1. Ha az összetevő először jelenik meg egy kérelemben:
    • Hozzon létre az összetevő példányát.
    • Hajtsd végre a tulajdonságok befecskendezését.
    • Hívja fel: OnInitialized{Async}. Ha egy hiányos Task van visszaadva, a Task-et várják, majd az összetevőt újrarenderelik. A szinkron metódust az aszinkron metódus előtt hívjuk meg.
  2. Hívja fel: OnParametersSet{Async}. Ha egy hiányos Task van visszaadva, a Task-et várják, majd az összetevőt újrarenderelik. A szinkron metódust az aszinkron metódus előtt hívjuk meg.
  3. Rendereljen az összes szinkron munkához, és fejezze be a Tasks.

Megjegyzés

Az életciklus-eseményekben végrehajtott aszinkron műveletek késleltethetik az összetevők renderelését vagy az adatok megjelenítését. További információért lásd a Hiányos aszinkron műveletek kezelése a renderelés szakaszában című fejezetet a jelen cikk későbbi részében.

A szülőösszetevők a gyermekösszetevők előtt jelennek meg, mert a renderelés határozza meg, hogy mely gyermekek vannak jelen. Ha szinkron szülő komponens inicializálását használja, a szülő komponens inicializálása garantáltan elsőként fejeződik be. Ha aszinkron szülőösszetevő-inicializálást használ, a szülő- és gyermekösszetevő-inicializálás befejezési sorrendje nem határozható meg, mert az a futó inicializálási kódtól függ.

A Razor összetevő életciklus-eseményei a Blazor

DOM-eseményfeldolgozás:

  1. Az eseménykezelő fut.
  2. Ha egy hiányos Task van visszaadva, a Task-et várják, majd az összetevőt újrarenderelik.
  3. Rendereljen az összes szinkron munkához, és fejezze be a Tasks.

DOM eseményfeldolgozás

A Render életciklusa:

  1. Kerülje a további renderelési műveleteket az összetevőn, ha az alábbi feltételek teljesülnek:
    • Nem ez az első renderelés.
    • ShouldRender visszaadja false.
  2. Hozd létre a render fa különbséget és rendereld a komponenst.
  3. Várjon a DOM frissítésére.
  4. Hívja fel: OnAfterRender{Async}. A szinkron metódust az aszinkron metódus előtt hívjuk meg.

renderelési életciklus

Az StateHasChanged hívások újrarenderelést eredményeznek. További információkért lásd: ASP.NET Core Razor összetevő renderelése.

Quiescence az előrenderelés során

A kiszolgálóoldali Blazor alkalmazásokban az előrenderelés megvárja nyugalmi állapotot, ami azt jelenti, hogy egy összetevő csak akkor jelenik meg, ha a renderelési fa összes összetevője befejezte a renderelést. A kiesés észrevehető késést okozhat a renderelésben, ha egy összetevő hosszú ideig futó feladatokat hajt végre az inicializálás és más életciklus-módszerek során, ami rossz felhasználói élményt eredményez. További információért lásd a Hiányos aszinkron műveletek kezelése a renderelés szakaszában című fejezetet a jelen cikk későbbi részében.

Paraméterek beállításakor (SetParametersAsync)

SetParametersAsync beállítja az összetevő szülője által a renderfában vagy az útvonalparaméterekben megadott paramétereket.

A metódus ParameterView paramétere az összetevő összetevőparaméterének értékeit tartalmazza minden alkalommal, amikor SetParametersAsync meghívják. A SetParametersAsync metódus felülírásával a fejlesztői kód közvetlenül kezelheti ParameterViewparamétereit.

A SetParametersAsync alapértelmezett implementációja az egyes tulajdonságok értékét az [Parameter] vagy [CascadingParameter] attribútummal állítja be,, amelynek megfelelő értéke van a ParameterView. A ParameterView-ban megfelelő értékkel nem rendelkező paraméterek változatlanok maradnak.

A kódnak általában az alaposztály metódusát (await base.SetParametersAsync(parameters);) kell meghívnia SetParametersAsyncfelülírásakor. Speciális helyzetekben a fejlesztői kód bármilyen módon képes értelmezni a bejövő paraméterek értékeit, ha nem invokálja az alaposztály metódusát. Nincs például szükség arra, hogy a bejövő paramétereket az osztály tulajdonságaihoz rendelje. A kód alaposztály-metódus meghívása nélkül történő strukturálásakor azonban a ComponentBase referenciaforrásra kell hivatkoznia, mert más életciklus-metódusokat hív meg, és összetett módon jeleníti meg az eseményindítókat.

Megjegyzés

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 az Ágak vagy címkék közötti váltás 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.

Ha az ComponentBase.SetParametersAsync inicializálási és renderelési logikájára szeretne támaszkodni, de nem szeretné feldolgozni a bejövő paramétereket, lehetősége van üres ParameterView átadni az alaposztály metódusának:

await base.SetParametersAsync(ParameterView.Empty);

Ha az eseménykezelők a fejlesztői kódban vannak megadva, szüntesse meg őket a felszámoláskor. További információért lásd: ASP.NET Core Razor összetevők eltávolítása.

Az alábbi példában ParameterView.TryGetValue hozzárendeli a Param paraméter értékét value, ha a Param útvonalparaméterének elemzése sikeres. Ha value nem null, az összetevő megjeleníti az értéket.

Bár az útvonalparaméter-egyeztetés nem érzékeny a kis- és nagybetűkre, TryGetValue csak a kis- és nagybetűket megkülönböztető paraméterneveknek felel meg az útvonalsablonban. Az alábbi példában /{Param?} kell használni az útvonalsablonban annak érdekében, hogy az érték TryGetValuelegyen, és ne /{param?}. Ha /{param?} van használva ebben a forgatókönyvben, TryGetValuefalse-t ad vissza, és message nincs beállítva egyik message sztringre sem.

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

Összetevő inicializálása (OnInitialized{Async})

OnInitialized és OnInitializedAsync kizárólag az összetevőpéldány teljes élettartama alatt inicializálnak egy összetevőt. A paraméterértékek és a paraméterértékek módosítása nem befolyásolhatja az ezekben a metódusokban végrehajtott inicializálást. A statikus beállítások betöltése például egy olyan legördülő listába, amely nem változik az összetevő élettartama során, és amely nem függ a paraméterértékektől, az egyik életciklus-metódusban történik. Ha a paraméterértékek vagy a paraméterértékek változásai hatással vannak az összetevő állapotára, használja inkább a OnParametersSet{Async}.

Ezek a metódusok akkor lesznek meghívva, ha az összetevő inicializálva van, miután megkapta a kezdeti paramétereket SetParametersAsync. A szinkron metódust az aszinkron metódus előtt hívjuk meg.

Ha szinkron szülőösszetevő-inicializálást használ, a szülő inicializálása garantáltan befejeződik a gyermekösszetevő inicializálása előtt. Ha aszinkron szülőösszetevő-inicializálást használ, a szülő- és gyermekösszetevő-inicializálás befejezési sorrendje nem határozható meg, mert az a futó inicializálási kódtól függ.

Szinkron művelet esetén bíráld felül a OnInitialized-t.

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

Aszinkron művelet végrehajtásához felülbírálja OnInitializedAsync, és használja a await operátort:

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

Ha az egyéni alaposztály egyéni inicializálási logikával van használva, hívja meg a OnInitializedAsync metódust az alaposztályon:

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

    await base.OnInitializedAsync();
}

Nem szükséges meghívni ComponentBase.OnInitializedAsync, hacsak nem használ egyéni alaposztályt egyéni logikával. További információ: Alaposztály életciklus-módszerei szakasz.

Az összetevőnek biztosítania kell, hogy érvényes állapotban legyen a rendereléshez, amikor OnInitializedAsync egy potenciálisan hiányos Task-re vár. Ha a metódus hiányos Taskad vissza, a szinkron módon befejezett résznek érvényes állapotban kell hagynia az összetevőt a rendereléshez. További információért lásd a ASP.NET Core Blazor szinkronizációs környezetének és az ASP.NET Core összetevők Razor eltávolításánakbevezető megjegyzéseit.

Blazor olyan alkalmazások, amelyek a tartalmat a kiszolgálón előre renderelik, kétszer hívják meg a(z) OnInitializedAsync:

  • Egyszer, amikor az összetevő kezdetben statikusan megjelenik a lap részeként.
  • Másodszor, amikor a böngésző megjeleníti az összetevőt.

Ha meg szeretné akadályozni, hogy az OnInitializedAsync fejlesztői kódja kétszer fusson az előrendeléskor, tekintse meg az állapotalapú újracsatlakozást szakasz előrendelése után. A szakasz tartalma az Blazor Web App-okra és az állapotalapú SignalRújracsatlakozásra fókuszál. Ha az állapotot az inicializálási kód végrehajtása során szeretné megőrizni az előrendelés során, tekintse meg az ASP.NET Core Blazor előrerendelt állapotmegőrzését.

Ha meg szeretné akadályozni, hogy az OnInitializedAsync fejlesztői kódja kétszer fusson az előrendeléskor, tekintse meg az állapotalapú újracsatlakozást szakasz előrendelése után. Bár a szakasz tartalma az Blazor Server és az állapotalapú SignalRújracsatlakozássalfoglalkozik, az üzemeltetett Blazor WebAssembly megoldások (WebAssemblyPrerendered) előrendelésének forgatókönyve hasonló feltételekkel és megközelítésekkel szolgál annak megakadályozására, hogy a fejlesztői kód kétszer végrehajtásra kerüljön. Ha az inicializálási kód végrehajtása során szeretné megőrizni az állapotot az előrendelés során, tekintse meg ASP.NET Core Razor összetevőinek integrálása MVC-vel vagy Razor Pages.

Bár egy Blazor alkalmazás előrerendezést végez, bizonyos műveletek, például a JavaScriptbe való hívás (JS interop) nem lehetséges. Előfordulhat, hogy az összetevőknek másképp kell renderelnie, ha előre van berendelve. További információért tekintse meg a Előfeldolgozás JavaScript együttműködéssel szakaszt.

Ha az eseménykezelők a fejlesztői kódban vannak megadva, szüntesse meg őket a felszámoláskor. További információért lásd: ASP.NET Core Razor összetevők eltávolítása.

A streaming alapú renderelést statikus kiszolgálóoldali rendereléssel (statikus SSR) vagy előzetes rendereléssel használja az olyan összetevők felhasználói élményének javítására, amelyek a OnInitializedAsync hosszú ideig futó aszinkron feladatokat hajtanak végre a teljes renderelés érdekében. További információ:

Paraméterek beállítása után (OnParametersSet{Async})

OnParametersSet vagy OnParametersSetAsync elnevezése:

  • Az összetevő OnInitialized-ban vagy OnInitializedAsync-ben történő inicializálása után.

  • Amikor a szülőkomponens újrarenderel és ellátja:

    • Ismert vagy primitív nem módosítható típusok, ha legalább egy paraméter módosult.
    • Összetett típusú paraméterek. A keretrendszer nem tudja, hogy egy összetett típusú paraméter értékei belsőleg mutálódnak-e, ezért a keretrendszer mindig módosítottként kezeli a paraméterkészletet egy vagy több összetett típusú paraméter jelenlétekor.

    A megjelenítési konvenciókról további információt a ASP.NET Core Razor összetevő renderelésicímű témakörben talál.

A szinkron metódust az aszinkron metódus előtt hívjuk meg.

A metódusok akkor is meghívhatók, ha a paraméterértékek nem változtak. Ez a viselkedés kiemeli, hogy a fejlesztőknek további logikát kell implementálniuk a metódusokon belül annak ellenőrzéséhez, hogy a paraméterértékek valóban megváltoztak-e, mielőtt újra inicializálják az adatokat vagy az adott paraméterektől függő állapotot.

A következő példaösszetevőhöz navigáljon az összetevő lapjára egy URL-címen:

  • A StartDate által kapott kezdési dátummal: /on-parameters-set/2021-03-19
  • Kezdési dátum nélkül, ahol StartDate az aktuális helyi időérték van hozzárendelve: /on-parameters-set

Megjegyzés

Egy összetevőútvonalon nem lehet DateTime paramétert korlátozni az útvonalkorláttal datetime, és a paramétert nem kötelezővé tenni. Ezért a következő OnParamsSet összetevő két @page direktívát használ az útválasztás kezeléséhez az URL-cím megadott dátumszegmensével és anélkül.

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

Paraméterek és tulajdonságértékek alkalmazásakor aszinkron munkának kell történnie a OnParametersSetAsync életciklus-esemény során:

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

Ha az egyéni alaposztály egyéni inicializálási logikával van használva, hívja meg a OnParametersSetAsync metódust az alaposztályon:

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

    await base.OnParametersSetAsync();
}

Nem szükséges meghívni ComponentBase.OnParametersSetAsync, hacsak nem használ egyéni alaposztályt egyéni logikával. További információ: Alaposztály életciklus-módszerei szakasz.

Az összetevőnek biztosítania kell, hogy érvényes állapotban legyen a rendereléshez, amikor OnParametersSetAsync egy potenciálisan hiányos Task-re vár. Ha a metódus hiányos Taskad vissza, a szinkron módon befejezett résznek érvényes állapotban kell hagynia az összetevőt a rendereléshez. További információért lásd a ASP.NET Core Blazor szinkronizációs környezetének és az ASP.NET Core összetevők Razor eltávolításánakbevezető megjegyzéseit.

Ha az eseménykezelők a fejlesztői kódban vannak megadva, szüntesse meg őket a felszámoláskor. További információért lásd: ASP.NET Core Razor összetevők eltávolítása.

Ha egy eldobható összetevő nem használja a CancellationToken-t, akkor a OnParametersSet-nek és OnParametersSetAsync-nek ellenőrizniük kell, hogy az összetevő meg lett-e semmisítve. Ha OnParametersSetAsync hiányos Taskad vissza, az összetevőnek biztosítania kell, hogy a módszer szinkron módon befejezett része érvényes állapotban hagyja az összetevőt a rendereléshez. További információért lásd a ASP.NET Core Blazor szinkronizációs környezetének és az ASP.NET Core összetevők Razor eltávolításánakbevezető megjegyzéseit.

Az útvonalparaméterekről és korlátozásokról további információt ASP.NET Core Blazor útválasztási és navigációscímű témakörben talál.

Néhány forgatókönyvben a teljesítmény javítása érdekében manuálisan történő implementálásra SetParametersAsync vonatkozó példa: ASP.NET Core Blazor renderelési teljesítményre vonatkozó ajánlott eljárások.

Összetevő renderelése után (OnAfterRender{Async})

OnAfterRender és OnAfterRenderAsync egy összetevő interaktív megjelenítése és a felhasználói felület frissítése után (például az elemek böngésző DOM-hoz való hozzáadása után) lesz meghívva. Ezen a ponton az elem- és összetevőhivatkozások kitöltésre kerültek. Ezen a szakaszon további inicializálási lépéseket hajthat végre a renderelt tartalommal, például JS a renderelt DOM-elemekkel interakcióba lépő interop hívásokat. A szinkron metódust az aszinkron metódus előtt hívjuk meg.

Ezek a metódusok nem lesznek meghívva a kiszolgáló előrendelése vagy statikus kiszolgálóoldali renderelése (statikus SSR) során, mert ezek a folyamatok nincsenek élő böngészőbeli DOM-hez csatolva, és már a DOM frissítése előtt befejeződnek.

A OnAfterRenderAsyncesetében az összetevő nem lesz automatikusan újrarendezve a visszaadott Task befejezése után, hogy elkerülje a végtelen renderelési ciklust.

OnAfterRender és OnAfterRenderAsync az összetevő renderelésének befejezése után lesz meghívva. Ezen a ponton az elem- és összetevőhivatkozások kitöltésre kerültek. Ezen a szakaszon további inicializálási lépéseket hajthat végre a renderelt tartalommal, például JS a renderelt DOM-elemekkel interakcióba lépő interop hívásokat. A szinkron metódust az aszinkron metódus előtt hívjuk meg.

Ezek a metódusok nem lesznek meghívva az előrendelés során, mert az előrendelés nem csatlakozik egy élő böngészőbeli DOM-hoz, és a DOM frissítése előtt már befejeződött.

A OnAfterRenderAsyncesetében az összetevő nem lesz automatikusan újrarendezve a visszaadott Task befejezése után, hogy elkerülje a végtelen renderelési ciklust.

A firstRender paramétere a OnAfterRender-hez és OnAfterRenderAsync-höz:

  • Az összetevőpéldány első renderelésekor beállítódik true-ra.
  • Az inicializálási munka elvégzésének biztosítására használható, hogy az csak egyszer történjen meg.

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

A AfterRender.razor minta a következő kimenetet hozza létre a konzolon a lap betöltésekor és a gomb kiválasztásakor:

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

Az aszinkron munkát közvetlenül a renderelés után kell elvégezni a OnAfterRenderAsync életciklus-esemény során:

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

Ha az egyéni alaposztály egyéni inicializálási logikával van használva, hívja meg a OnAfterRenderAsync metódust az alaposztályon:

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

    await base.OnAfterRenderAsync(firstRender);
}

Nem szükséges meghívni ComponentBase.OnAfterRenderAsync, hacsak nem használ egyéni alaposztályt egyéni logikával. További információ: Alaposztály életciklus-módszerei szakasz.

Még ha Task-ből OnAfterRenderAsync-t is ad vissza, a keretrendszer nem ütemez további renderelési ciklust az összetevőhöz, miután a feladat befejeződik. Ennek célja, hogy elkerülje a végtelen renderelési ciklust. Ez eltér a többi életciklus-módszertől, amelyek egy újabb renderelési ciklust ütemeznek a visszaadott Task befejeződése után.

OnAfterRender és OnAfterRenderAsyncnem hívódnak meg az előrenderelési folyamat során a kiszolgálón. A metódusok akkor hívódnak meg, amikor az összetevő interaktívan jelenik meg az előrenderelés után. Amikor az alkalmazás előrenderel:

  1. Az összetevő a kiszolgálón hajt végre néhány statikus HTML-korrektúrát a HTTP-válaszban. Ebben a fázisban a OnAfterRender és a OnAfterRenderAsync nem lesznek meghívva.
  2. Amikor a Blazor szkript (blazor.{server|webassembly|web}.js) elindul a böngészőben, az összetevő interaktív renderelési módban újraindul. Az összetevő újraindítása után a és a meghívásra kerülnek, mert az alkalmazás már nincs az előrenderelési fázisban.

Ha az eseménykezelők a fejlesztői kódban vannak megadva, szüntesse meg őket a felszámoláskor. További információért lásd: ASP.NET Core Razor összetevők eltávolítása.

Alaposztály életciklus-módszerei

Blazoréletciklus-metódusainak felülírásakor nem szükséges meghívni az alaposztály életciklus-metódusait ComponentBaseesetén. Az összetevőnek azonban meg kell hívnia az alaposztály felülírt életciklus-metódusát a következő helyzetekben:

  • A ComponentBase.SetParametersAsyncfelülírásakor a await base.SetParametersAsync(parameters);-et általában azért hívjuk meg, mert az alaposztály metódusa más életciklus-metódusokat hív meg és összetett módon elindítja a renderelést. További információért lásd a A paraméterek beállítása (SetParametersAsync) szakaszt.
  • Ha az alaposztály-metódus olyan logikát tartalmaz, amelyet végre kell hajtani. A kódtár felhasználói általában alaposztály-életciklus-metódusokat neveznek az alaposztály öröklésekor, mivel a kódtár alaposztályai gyakran egyéni életciklus-logikával rendelkeznek. Ha az alkalmazás egy tár alaposztályát használja, útmutatásért tekintse meg a könyvtár dokumentációját.

Az alábbi példában base.OnInitialized(); hívjuk meg, hogy az alaposztály OnInitialized metódusa végrehajtásra kerüljön. A hívás nélkül a BlazorRocksBase2.OnInitialized nem kerül végrehajtásra.

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

Állapotváltozások (StateHasChanged)

StateHasChanged értesíti az összetevőt, hogy az állapota megváltozott. Adott esetben a StateHasChanged meghívása egy újrarenderelést sorba állít, amely akkor hajtódik végre, amikor az alkalmazás fő szála nincs terhelés alatt.

StateHasChanged EventCallback metódusok esetén a rendszer automatikusan meghívja. További információ az eseményvisszahívásokról: ASP.NET Core Blazor eseménykezelési.

További információkért az összetevők rendereléséről és a StateHasChangedhívásáról, beleértve azt is, hogy mikor kell meghívni a ComponentBase.InvokeAsync-et, lásd a ASP.NET Core Razor összetevő renderelésicímű témakört.

Hiányos aszinkron műveletek kezelése rendereléskor

Előfordulhat, hogy az életciklus-eseményekben végrehajtott aszinkron műveletek nem fejeződtek be az összetevő renderelése előtt. Előfordulhat, hogy az objektumok null vagy hiányosan töltik ki az adatokat az életciklus-metódus végrehajtása közben. Adja meg a renderelési logikát az objektumok inicializálásának ellenőrzéséhez. A helyőrző felhasználói felület elemeinek megjelenítése (például egy betöltési üzenet), amíg az objektumok a nullállapoton vannak.

Az alábbi Slow összetevőben a OnInitializedAsync felülírásra kerül, hogy aszinkron módon hajtsa végre a hosszú ideig futó feladatot. Miközben isLoadingtrue, a felhasználónak egy töltési üzenet jelenik meg. Miután a Task által visszaadott OnInitializedAsync befejeződött, az összetevőt újrarendereljük a frissített állapottal, és megjelenik a "Finished!" üzenet.

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

Az előző összetevő egy isLoading változót használ a betöltési üzenet megjelenítéséhez. Hasonló megközelítést használunk egy olyan összetevőhöz, amely adatokat tölt be egy gyűjteménybe, és ellenőrzi, hogy a gyűjtemény null-e a betöltési üzenet bemutatásához. Az alábbi példa ellenőrzi a movies gyűjteményt, hogy null megjelenítse-e a betöltési üzenetet, vagy a filmek gyűjteményét jelenítse meg.

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

@code {
    private Movies[]? movies;

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

Az előrenderelés megvárja nyugalmi állapot, ami azt jelenti, hogy egy összetevő csak akkor renderelődik, ha a renderelési fa összes összetevője befejezte a renderelést. Ez azt jelenti, hogy egy betöltési üzenet nem jelenik meg, miközben a gyermekösszetevő OnInitializedAsync metódusa hosszú ideig futó feladatot hajt végre az előrenderelés során. Ennek a viselkedésnek a bemutatásához helyezze az előző Slow összetevőt egy tesztalkalmazás Home összetevőjére:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<Slow />

Megjegyzés

Bár az ebben a szakaszban szereplő példák az OnInitializedAsync életciklus-módszert ismertetik, az előrendelés során végrehajtott egyéb életciklus-metódusok késleltethetik az összetevők végleges renderelését. Csak a OnAfterRender{Async} nincsen végrehajtva az előrendelés során, és immunis a nyugalmi idő miatti késésekre.

Az előrendelés során a Home összetevő csak akkor jelenik meg, ha a Slow összetevő megjelenik, ami tíz másodpercet vesz igénybe. A felhasználói felület ebben a tíz másodperces időszakban üres, és nincs betöltési üzenet. Az előrendelés után megjelenik a Home összetevő, és megjelenik a Slow összetevő betöltési üzenete. További tíz másodperc elteltével a Slow összetevő végül megjeleníti a kész üzenetet.

Ahogy az előző bemutató is szemlélteti, a nyugalom a prerenderelés során rossz felhasználói élményt eredményez. A felhasználói élmény javítása érdekében először implementálja a streamelő renderelést, hogy elkerülje az aszinkron feladat elvégzésére való várakozást az előrenderelés során.

Adja hozzá a [StreamRendering] attribútumot a Slow összetevőhöz (használja a [StreamRendering(true)] a .NET 8-ban):

@attribute [StreamRendering]

Ha a Home összetevő előrerendezést használ, a Slow összetevő gyorsan megjelenik a betöltési üzenettel. A Home összetevő nem vár tíz másodpercig, amíg a Slow összetevő befejezi a renderelést. Az előrendelés végén megjelenő kész üzenetet azonban a betöltési üzenet váltja fel, míg az összetevő végül renderel, ami újabb tíz másodperces késés. Ez azért fordul elő, mert a Slow összetevő kétszer renderel, és LoadDataAsync kétszer hajtja végre. Amikor egy összetevő hozzáfér az erőforrásokhoz, például a szolgáltatásokhoz és az adatbázisokhoz, a szolgáltatáshívások és az adatbázis-lekérdezések kettős végrehajtása nemkívánatos terhelést okoz az alkalmazás erőforrásain.

A betöltési üzenet kettős renderelésének és a szolgáltatás- és adatbázis-hívások újrafuttatásának kezeléséhez őrizze meg az előrerendezett állapotot PersistentComponentState az összetevő végleges rendereléséhez, ahogyan az Slow összetevő alábbi frissítéseiben látható:

@page "/slow"
@attribute [StreamRendering]

<h2>Slow Component</h2>

@if (Data is null)
{
    <div><em>Loading...</em></div>
}
else
{
    <div>@Data</div>
}

@code {
    [PersistentState]
    public string? Data { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Data ??= await LoadDataAsync();
    }

    private async Task<string> LoadDataAsync()
    {
        await Task.Delay(5000);
        return "Finished!";
    }
}
@page "/slow"
@attribute [StreamRendering]
@implements IDisposable
@inject PersistentComponentState ApplicationState

<h2>Slow Component</h2>

@if (data is null)
{
    <div><em>Loading...</em></div>
}
else
{
    <div>@data</div>
}

@code {
    private string? data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
        {
            data = await LoadDataAsync();
        }
        else
        {
            data = restored!;
        }

        // Call at the end to avoid a potential race condition at app shutdown
        persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson(nameof(data), data);

        return Task.CompletedTask;
    }

    private async Task<string> LoadDataAsync()
    {
        await Task.Delay(5000);
        return "Finished!";
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

A streamelési renderelés és az állandó összetevő állapotának kombinálásával:

  • A szolgáltatások és adatbázisok csak egyetlen hívást igényelnek az összetevők inicializálásához.
  • Az összetevők gyorsan renderelik felhasználói felületeiket az üzenetek betöltésekor a hosszú ideig futó feladatok során a legjobb felhasználói élmény érdekében.

További információ:

:::moniker-end

A nyugalmi állapot a prerenderezés során rossz felhasználói élményt eredményez. A késés a .NET 8-hoz vagy újabbhoz szánt alkalmazásokban kezelhető egy adatfolyam-alapú renderelésnevű funkcióval, amelyet általában az összetevő állapotának megtartásával kombinálnak az előrendelés során, elkerülendő az aszinkron feladat befejezésére való várakozást. A .NET 8-nál korábbi verzióiban egy hosszú ideig futó háttérfeladat végrehajtása, amely betölti az adatokat a végleges renderelés után, a nyugalmi állapot miatti hosszú renderelési késést is képes kezelni.

Hibák kezelése

Az életciklus-metódus végrehajtása során előforduló hibák kezeléséről további információt ASP.NET Core Blazor-alkalmazások hibáinak kezelésecímű témakörben talál.

Állapotmegőrző újracsatlakozás az előrenderelés után

A szerveroldali előrendezés során egy összetevő kezdetben statikusan van előrendezve, mint az oldal része. Miután a böngésző létrehoz egy SignalR kapcsolatot a kiszolgálóval, az összetevő ismét és interaktív lesz. Ha az összetevő inicializálására szolgáló OnInitialized{Async} életciklus-módszer jelen van, a módszer kétszer kerül végrehajtásra .

  • Ha az összetevő statikusan előre van berendítve.
  • A kiszolgálókapcsolat létrejötte után.

Ez a felhasználói felületen megjelenő adatok észrevehető változásához vezethet, amikor az összetevőt végre rendereli. A viselkedés elkerülése érdekében adjon meg egy azonosítót, hogy a gyorsítótárba helyezze az állapotot az előzetes renderelés során, és kérje le az állapotot az előzetes renderelés után.

Az alábbi kód egy olyan WeatherForecastService-t mutat be, amely elkerüli az adatmegjelenítés előrenderelés miatti módosítását. A várt Delay (await Task.Delay(...)) rövid késleltetést szimulál, mielőtt adatokat ad vissza a GetForecastAsync metódusból.

Adjon hozzá IMemoryCache szolgáltatásokat a szolgáltatásgyűjteményhez AddMemoryCache, amely az alkalmazás Program fájljában található.

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

A RenderModekapcsolatos további információkért lásd a ASP.NET Core BlazorSignalR útmutatóját.

Ebben a szakaszban a Blazor Web App és az állapotalapú SignalRújracsatlakozás foglalkozik. Ha az állapotot az inicializálási kód végrehajtása során szeretné megőrizni az előrendelés során, tekintse meg az ASP.NET Core Blazor előrerendelt állapotmegőrzését.

Bár ebben a szakaszban a tartalom a Blazor Server és az állapotalapú SignalRújracsatlakozásifoglalkozik, az üzemeltetett Blazor WebAssembly megoldások (WebAssemblyPrerendered) előrendelésének forgatókönyve hasonló feltételekkel és megközelítésekkel akadályozza meg a fejlesztői kód kétszeri végrehajtását. Ha az inicializálási kód végrehajtása során szeretné megőrizni az állapotot az előrendelés során, tekintse meg ASP.NET Core Razor összetevőinek integrálása MVC-vel vagy Razor Pages.

Előrerendezés JavaScript-interop használatával

Ez a szakasz azokra a kiszolgálóoldali alkalmazásokra vonatkozik, amelyek előre renderelik a Razor összetevőket. Az előrenderelést a Prerender ASP.NET Core Razor összetevőktárgyalják.

Ez a szakasz azokra a kiszolgálóoldali alkalmazásokra vonatkozik, amelyek előre renderelik a Razor összetevőket. Az előrenderelést a Prerender ASP.NET Core Razor összetevőktárgyalják.

Megjegyzés

A belső navigációja a Blazor Web App esetén nem igényli új oldaltartalom kérését a szervertől. Ezért a belső lapkérelmek esetében nem történik meg az előfeldolgozás. Ha az alkalmazás az interaktív útválasztást alkalmazza, végezzen teljes oldal újratöltést az előrerenderelő viselkedést bemutató komponensek példáihoz. További információ: ASP.NET Core Blazor előre beállított állapotmegőrzés.

Ez a szakasz a kiszolgálóoldali alkalmazásokra és az Blazor WebAssembly alkalmazásokra vonatkozik, amelyek előrenderelik az Razor összetevőket. Az előrenderelés szerepel a ASP.NET Core Razor komponenseinek integrálásában az MVC-vel vagy a Razor oldalakkal.

Az előzetes renderelés során a JavaScript hívása (JS) nem lehetséges. Az alábbi példa bemutatja, hogyan használható JS interop egy összetevő inicializálási logikájának részeként az előrendeléssel kompatibilis módon.

A következő scrollElementIntoView függvény:

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

Ha a IJSRuntime.InvokeAsync az összetevőkódban meghívja a JS függvényt, akkor a ElementReference csak a OnAfterRenderAsync alatt kerül felhasználásra, és nem egy korábbi életciklus-metódusban, mert az összetevő renderelése előtt nincs HTML DOM-elem.

StateHasChanged (referenciaforrás) az összetevő újrarenderelésének sorba állítására szolgál az JS interop hívásból kapott új állapottal (további információ: ASP.NET Core Razor komponens renderelése). A rendszer nem hoz létre végtelen hurkot, mert StateHasChanged csak akkor hívjuk meg, ha scrollPositionnull.

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

Az előző példa egy globális függvénnyel terheli a klienst. A gyártási alkalmazások jobb módszeréért lásd: JavaScript-elkülönítés a JavaScript-modulokban.

Megszakítható háttérmunka

Az összetevők gyakran hosszú ideig futó háttérmunkát végeznek, például hálózati hívásokat kezdeményeznek (HttpClient) és együttműködnek az adatbázisokkal. Célszerű leállítani a háttérmunkát a rendszererőforrások több helyzetben történő megőrzéséhez. A háttérbeli aszinkron műveletek például nem állnak le automatikusan, amikor egy felhasználó ellép egy összetevőtől.

Egyéb okok, amelyek miatt a háttérmunkaelemek lemondást igényelhetnek, a következők lehetnek:

  • A háttérfeladat végrehajtása hibás bemeneti adatokkal vagy feldolgozási paraméterekkel kezdődött.
  • A háttérbeli munkaelemek végrehajtásának jelenlegi készletét új munkaelemkészletre kell cserélni.
  • A feladatok jelenleg történő végrehajtásának prioritását módosítani kell.
  • Az alkalmazást le kell állítani a kiszolgáló újbóli üzembe helyezéséhez.
  • A kiszolgáló erőforrásai korlátozottak lesznek, ami szükségessé teszi a háttérmunkaelemek átütemezését.

Megszakítható háttérmunkaminta implementálása egy összetevőben:

Az alábbi példában:

  • await Task.Delay(10000, cts.Token); hosszú ideig futó aszinkron háttérmunkát jelöl.
  • BackgroundResourceMethod egy hosszú ideig futó háttérmetódus, amely nem indul el, ha a Resource a metódus meghívása előtt el lett távolítva.

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

Ha egy betöltési jelzőt szeretne megjeleníteni a háttérmunka során, használja az alábbi megközelítést.

Hozzon létre egy betöltésjelző összetevőt egy Loading paraméterrel, amely alárendelt tartalmat tud megjeleníteni egy RenderFragment-ben. Loading A paraméter esetében:

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

Ha CSS-stílusokat szeretne betölteni a mutatóhoz, adja hozzá a stílusokat a <head> tartalmához az HeadContent összetevővel. További információ: Vezérlőfej tartalma ASP.NET Core-alkalmazásokbanBlazor.

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

...

Burkolja be a komponens Razor jelölését a ContentLoading komponenssel, és adjon át egy értéket egy C# mezőből a Loading paraméternek, amikor az összetevő végzi az inicializálási műveleteit.

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

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

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

    ...
}

Blazor Server újracsatlakozási események

A cikkben tárgyalt összetevők életciklus-eseményei külön működnek kiszolgálóoldali újracsatlakozási eseménykezelőktől. Ha az ügyfélhez való SignalR kapcsolat megszakad, csak a felhasználói felület frissítései szakadnak meg. A felhasználói felület frissítései a kapcsolat újbóli létrehozásakor újraindulnak. További információ az áramköri kezelő eseményeiről és konfigurációjáról: lásd az ASP.NET Core BlazorSignalR útmutatót.

További erőforrások

Razor összetevő életciklusán kívüli kivételeket kezel