Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A cikk tartalma
Megjegyzés
Ez nem a cikk legújabb verziója. Az aktuális kiadást lásd a cikk .NET 9-es verziójában.
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.
Fontos
Ezek az információk egy olyan előzetes termékre vonatkoznak, amelyet a kereskedelmi forgalomba kerülés előtt jelentősen módosíthatnak. A Microsoft nem vállal kifejezett vagy hallgatólagos szavatosságot az itt megadott információkra vonatkozóan.
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.
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 válassza 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:
- 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
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.
- Hívja
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. - 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.
DOM-eseményfeldolgozás:
- Az eseménykezelő fut.
- Ha egy hiányos Task van visszaadva, a Task-et várják, majd az összetevőt újrarenderelik.
- Rendereljen az összes szinkron munkához, és fejezze be a Tasks.
A Render
életciklusa:
- 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
visszaadjafalse
.
- Hozza létre a renderelési fa diffjét (különbség) és renderelje az összetevőt.
- Várjon a DOM frissítésére.
- Hívja
OnAfterRender{Async}
. A szinkron metódust az aszinkron metódus előtt hívjuk meg.
Az StateHasChanged
hívások újrarenderelést eredményeznek. További információkért lásd: ASP.NET Core Razor összetevő renderelése.
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.
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 válassza 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);
}
}
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 futtatása közben szeretné megőrizni az előzetes megjelenítés során, tekintse meg a Prerender ASP.NET Core Razor összetevőket.
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ó:
OnParametersSet vagy OnParametersSetAsync elnevezése:
Az összetevő OnInitialized-ban vagy OnInitializedAsync-ben történő inicializálása után.
Amikor a szülőösszetevő újrarendeli és szolgáltatja a következőt:
- 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.
Példa a SetParametersAsync
manuális implementálására egyes forgatókönyvekben a teljesítmény javítása érdekében: ASP.NET Core Blazor teljesítményre vonatkozó ajánlott eljárások.
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:
- 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.
- 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.
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!");
}
}
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.
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. Amíg isLoading
true
, egy betöltési üzenet jelenik meg a felhasználó számára. 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 {
[SupplyParameterFromPersistentComponentState]
public string? Data { get; set; }
protected override async Task OnInitializedAsync()
{
Data ??= await LoadDataAsync();
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
}
@page "/slow"
@attribute [StreamRendering]
@implements IDisposable
@inject PersistentComponentState ApplicationState
<h2>Slow Component</h2>
@if (data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@data</div>
}
@code {
private string? data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
{
data = await LoadDataAsync();
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(data), data);
return Task.CompletedTask;
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
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.0 előtti verziókban egy hosszú ideig futó háttérfeladat végrehajtása, amely az adatokat betölti a végleges renderelés után, csökkentheti a hosszú renderelési késedelmet a csendesség miatt.
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.
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 futtatása közben szeretné megőrizni az előzetes megjelenítés során, tekintse meg a Prerender ASP.NET Core Razor összetevőket.
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.
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őrenderelé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ó: Prerender ASP.NET Core Razor-összetevők.
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:
- Görgessen az átadott elemhez:
scrollIntoView
. - Az elem
top
tulajdonságértékét adja vissza agetBoundingClientRect
metódusból.
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 scrollPosition
null
.
PrerenderedInterop.razor
:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
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.
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:
- Használjon CancellationTokenSource és CancellationToken.
- Az összetevő ártalmatlanításakor, illetve amikor bármikor le szeretné mondani a tevékenységet a jogkivonat manuális visszavonásával, hívja meg a
CancellationTokenSource.Cancel
-t, hogy jelezze, hogy a háttérmunkát törölni kell. - Az aszinkron hívás visszatérése után hívja meg a ThrowIfCancellationRequested-t a tokenen.
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 aResource
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ési jelző összetevőt egy Loading
paraméterrel, amely megjelenítheti a gyermektartalmakat egy RenderFragment.
Loading
A paraméter esetében:
- Amikor
true
egy betöltési jelző jelenik meg. - Amikor
false
rendereli az összetevő tartalmát (ChildContent
). További információért lásd Gyermek tartalom megjelenítési töredékek.
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);
}
...
}
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.
ASP.NET Core -visszajelzés
A(z) ASP.NET Core egy nyílt forráskód projekt. Visszajelzés adásához válasszon egy hivatkozást: