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.
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ót a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .
Optimalizálja a renderelési sebességet a renderelési számítási feladatok minimalizálása és a felhasználói felület válaszkészségének javítása érdekében, ami a felhasználói felületi megjelenítés sebességének tízszeresét vagy nagyobb javulását eredményezheti.
Az összetevő-részhalmazok szükségtelen renderelésének elkerülése
Előfordulhat, hogy a szülőösszetevő megjelenítési költségeinek jelentős részét csökkentheti úgy, hogy kihagyja a gyermekösszetevők újrarenderelését egy esemény bekövetkezésekor. Csak azokra a részfákra legyen tekintettel, amelyeket kihagy, mivel ezek nagy számítási igényűek a renderelés szempontjából, és felhasználói felületi késést okoznak.
Futásidőben az összetevők egy hierarchiában léteznek. A gyökérösszetevő (az első betöltött összetevő) gyermekösszetevőkkel rendelkezik. A gyökér gyermekei saját alkomponensekkel rendelkeznek, és így tovább. Esemény bekövetkezésekor ( például egy gombot választó felhasználónál) a következő folyamat határozza meg, hogy mely összetevőket kell újrarendezni:
- Az eseményt a rendszer az esemény kezelőjét megjelenítő összetevőnek küldi el. Az eseménykezelő végrehajtása után a rendszer újrarendezi az összetevőt.
- Egy összetevő újrarendezésekor a paraméterértékek új másolatát látja el az egyes gyermekösszetevőknek.
- A paraméterértékek új készletének beérkezése után dönti el, Blazor hogy újrarendezi-e az összetevőt. Az összetevők újrarenderelődnek, ha a
ShouldRendervisszaadja atrueértéket, ami az alapértelmezett viselkedés, kivéve, ha felül van bírálva, és a paraméterértékek módosulhattak, például módosítható objektumok esetén.
Az előző sorozat utolsó két lépése rekurzív módon folytatódik az összetevőhierarchiában. Sok esetben a rendszer a teljes részhalmazt újrarendezi. A magas szintű összetevőket célzó események költséges átrendezést okozhatnak, mivel a magas szintű összetevő alatti összes összetevőnek újrarendezést kell végeznie.
Ha meg szeretné akadályozni a rekurzió egy adott részhalmazba való megjelenítését, használja az alábbi módszerek egyikét:
- Győződjön meg arról, hogy a gyermekösszetevő paraméterei bizonyos nem módosítható típusok†, például
string,int,boolésDateTime. A módosítások észlelésére szolgáló beépített logika automatikusan kihagyja az újrarendezést, ha a nem módosítható paraméterértékek nem változtak. Ha megjelenít egy gyermekkomponenst a<Customer CustomerId="item.CustomerId" />-nal, ahol aCustomerIdinttípusú, akkor aCustomerkomponens csak akkor kerül újra renderelésre, ha aitem.CustomerIdmegváltozik. - Felülbírálás
ShouldRender, visszatérésfalse:- Ha a paraméterek nemprimittív típusok vagy nem támogatott nem módosítható típusok†, például összetett egyéni modelltípusok vagy RenderFragment értékek, és a paraméterértékek nem változtak,
- Ha olyan csak felhasználói felületet használó összetevőt hoz létre, amely a kezdeti renderelés után nem változik, a paraméter értékének változásaitól függetlenül.
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 légi járatkeresési eszköz példája magánmezőket használ a változások észleléséhez szükséges információk nyomon követéséhez. Az előző bejövő járatazonosító (prevInboundFlightId) és az előző kimenő járatazonosító (prevOutboundFlightId) nyomon követi a következő lehetséges összetevő-frissítés adatait. Ha a repülési azonosítók bármelyike megváltozik, amikor az összetevő paraméterei OnParametersSetvannak beállítva, a rendszer újrarendezi az összetevőt, mert shouldRendertrueértékre van állítva. Ha shouldRender a repülési azonosítók ellenőrzése után false-re van kiértékelve, elkerülhető a költséges újrarajzolás.
@code {
private int prevInboundFlightId = 0;
private int prevOutboundFlightId = 0;
private bool shouldRender;
[Parameter]
public FlightInfo? InboundFlight { get; set; }
[Parameter]
public FlightInfo? OutboundFlight { get; set; }
protected override void OnParametersSet()
{
shouldRender = InboundFlight?.FlightId != prevInboundFlightId
|| OutboundFlight?.FlightId != prevOutboundFlightId;
prevInboundFlightId = InboundFlight?.FlightId ?? 0;
prevOutboundFlightId = OutboundFlight?.FlightId ?? 0;
}
protected override bool ShouldRender() => shouldRender;
}
Az eseménykezelő shouldRender is beállíthatja true. A legtöbb összetevő esetében általában nem szükséges az egyes eseménykezelők szintjén az újrarendezés meghatározása.
További információt a következő források tartalmaznak:
Virtualizálás
Ha nagy mennyiségű felhasználói felületet jelenít meg egy cikluson belül, például egy több ezer bejegyzést tartalmazó listát vagy rácsot, a renderelési műveletek mennyisége a felhasználói felület renderelésének késéséhez vezethet. Mivel a felhasználó egyszerre csak kis számú elemet lát görgetés nélkül, gyakran felesleges időt fordítani a jelenleg nem látható elemek renderelésére.
Blazor biztosítja a Virtualize<TItem> összetevőt egy tetszőlegesen nagy lista megjelenésének és görgetésének létrehozásához, miközben csak az aktuális görgetési nézeten belüli listaelemeket jeleníti meg. Egy összetevő például megjeleníthet egy 100 000 bejegyzést tartalmazó listát, de csak 20 látható elem megjelenítési költségét fizeti.
További információért lásd a ASP.NET Core Razor összetevő-virtualizáláscímű részt.
Egyszerűsített, optimalizált összetevők létrehozása
A legtöbb Razor összetevő nem igényel agresszív optimalizálási erőfeszítéseket, mert a legtöbb összetevő nem ismétlődik a felhasználói felületen, és nem kerül újrarendbe nagy gyakorisággal. Például a @page direktívával rendelkező, útválasztható összetevők és a felhasználói felület magas szintű elemeinek, például párbeszédpanelek vagy űrlapok megjelenítésére használt összetevők valószínűleg csak egyenként jelennek meg, és csak a felhasználói gesztusokra reagálva rajzolódnak újra. Ezek az összetevők általában nem hoznak létre nagy renderelési számítási feladatot, így szabadon használhatja a keretrendszerfunkciók bármilyen kombinációját anélkül, hogy a teljesítmény miatt aggódnia kéne.
Vannak azonban olyan gyakori forgatókönyvek, amikor az összetevők nagy léptékben ismétlődnek, és gyakran gyenge felhasználói felületi teljesítményt eredményeznek:
- Nagyméretű beágyazott űrlapok több száz egyedi elemből, például bemenetekből vagy címkékből.
- Több száz sorból vagy több ezer cellából álló rácsok.
- Pontdiagramok több millió adatponttal.
Ha az egyes elemeket, cellákat vagy adatpontokat külön összetevőpéldányként modellezi, gyakran olyan sok van belőlük, hogy a megjelenítési teljesítményük kritikus fontosságúvá válik. Ez a szakasz tanácsokat nyújt az ilyen összetevők könnyűvé tételéhez, hogy a felhasználói felület gyors és rugalmas maradjon.
Több ezer összetevőpéldány elkerülése
Minden összetevő egy külön sziget, amely a szülőktől és a gyermekektől függetlenül képes megjelenítésre. A felhasználói felület összetevők hierarchiájára való felosztásával átveheti az irányítást a felhasználói felületi megjelenítés részletessége felett. Ez jó vagy gyenge teljesítményt eredményezhet.
A felhasználói felület különálló összetevőkre való felosztásával a UI kisebb részei újrarenderelhetők, amikor események történnek. Egy több sorból álló táblázatban, amelyekben minden sorban egy gomb található, előfordulhat, hogy az egész oldal vagy táblázat helyett egy gyermekösszetevőt használva csak az egysoros átrendezőt használhatja. Az egyes összetevők azonban további memóriát és processzorterhelést igényelnek a független állapot és a renderelési életciklus kezeléséhez.
A ASP.NET Core termékegység-mérnökei által elvégzett teszt során egy Blazor WebAssembly-alkalmazásban körülbelül 0,06 ms renderelési többletterhelést észleltek egy összetevőpéldányonként. A tesztalkalmazás egy egyszerű összetevőt renderelt, amely három paramétert fogad el. Belsőleg a többletterhelés nagyrészt az összetevőnkénti állapot szótárakból való lekérésének, valamint a paraméterek átadásának és fogadásának köszönhető. A szorzással láthatja, hogy 2000 további összetevőpéldány hozzáadása 0,12 másodpercet adna hozzá a megjelenítési időhöz, és a felhasználói felület lassan kezdené érezni magát a felhasználók számára.
Lehetséges könnyebbé tenni az összetevőket, hogy több legyen belőlük. A hatékonyabb technika azonban gyakran elkerüli, hogy ennyi összetevőt jelenítsen meg. A következő szakaszok két módszert ismertetnek.
További információ a memóriakezelésről: Memória kezelése az üzembe helyezett ASP.NET Core kiszolgálóoldali Blazor alkalmazásokban.
Gyermekösszetevők beágyazása a szülőkbe: Tekintse meg a szülőösszetevő következő részét, amely körben jeleníti meg a gyermekösszetevőket:
<div class="chat">
@foreach (var message in messages)
{
<ChatMessageDisplay Message="message" />
}
</div>
ChatMessageDisplay.razor:
<div class="chat-message">
<span class="author">@Message.Author</span>
<span class="text">@Message.Text</span>
</div>
@code {
[Parameter]
public ChatMessage? Message { get; set; }
}
Az előző példa jól teljesít, ha több ezer üzenet nem jelenik meg egyszerre. Ha egyszerre több ezer üzenetet szeretne megjeleníteni, vegye fontolóra, hogy ne hagyja el a különálló ChatMessageDisplay összetevő különválasztását. Ehelyett ágyazza be a gyermekkomponenst a szülőbe. Az alábbi megközelítés elkerüli az egyes összetevőkhöz kapcsolódó többletterhelést azáltal, hogy ennyi gyermekkomponenst megjelenít, de ez azzal a költséggel jár, hogy elveszíti az egyes gyermekösszetevők önálló újrarajzolásának képességét.
<div class="chat">
@foreach (var message in messages)
{
<div class="chat-message">
<span class="author">@message.Author</span>
<span class="text">@message.Text</span>
</div>
}
</div>
Újrafelhasználható RenderFragments kód meghatározása: Előfordulhat, hogy az alösszetevőket a renderelési logika újrafelhasználása céljából különíti el. Ha ez a helyzet, további összetevők implementálása nélkül is létrehozhat újrafelhasználható renderelési logikát. Bármely összetevő @code blokkjában határozzon meg egy RenderFragment. A töredék megjelenítése tetszőleges helyről, ahányszor csak szükséges:
@RenderWelcomeInfo
<p>Render the welcome content a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = @<p>Welcome to your new app!</p>;
}
A RenderTreeBuilder kód több összetevőre való újrafelhasználásához deklarálja a RenderFragmentpublic és static:
public static RenderFragment SayHello = @<h1>Hello!</h1>;
SayHello az előző példában egy nem kapcsolódó összetevőből hívható meg. Ez a technika olyan újrafelhasználható kódrészletek kódtárainak létrehozásához hasznos, amelyek összetevőnkénti többletterhelés nélkül renderelnek.
RenderFragment meghatalmazottak elfogadhatnak paramétereket. A következő összetevő továbbítja az üzenetet (message) a RenderFragment meghatalmazottnak:
<div class="chat">
@foreach (var message in messages)
{
@ChatMessageDisplay(message)
}
</div>
@code {
private RenderFragment<ChatMessage> ChatMessageDisplay = message =>
@<div class="chat-message">
<span class="author">@message.Author</span>
<span class="text">@message.Text</span>
</div>;
}
Az előző megközelítés újra felhasználja a renderelési logikát összetevőnkénti többletterhelés nélkül. A megközelítés azonban nem teszi lehetővé a felhasználói felület részterületének egymástól függetlenül történő frissítését, és nem tudja kihagyni a felhasználói felület részterületének megjelenítését, amikor a szülő rendereli, mert nincs összetevőhatár. A RenderFragment meghatalmazotthoz való hozzárendelés csak Razor összetevőfájlokban (.razor) támogatott.
Olyan nem statikus mező, metódus vagy tulajdonság esetében, amelyekre nem hivatkozhat mező inicializáló, például TitleTemplate a következő példában, a RenderFragmentmező helyett egy tulajdonságot használjon:
protected RenderFragment DisplayTitle =>
@<div>
@TitleTemplate
</div>;
Nem kap túl sok paramétert
Ha egy összetevő rendkívül gyakran ismétlődik, például több száz vagy több ezer alkalommal, az egyes paraméterek átadásával és fogadásával járó többletterhelés fel fog nőni.
Ritka, hogy a túl sok paraméter súlyosan korlátozza a teljesítményt, de ez tényező lehet. Egy olyan TableCell összetevő esetében, amely egy rácson belül 4000-szer jelenik meg, az összetevőnek átadott paraméterek körülbelül 15 ms-t adnak hozzá a teljes renderelési költséghez. Tíz paraméter átadása körülbelül 150 ms-t igényel, és felhasználói felületi megjelenítési késést okoz.
A paraméterterhelés csökkentéséhez csomagoljon össze több paramétert egy egyéni osztályban. Egy táblázatcella-összetevő például elfogadhat egy közös objektumot. Az alábbi példában a Data minden cellához eltérő, de az Options minden cellapéldányban gyakori:
@typeparam TItem
...
@code {
[Parameter]
public TItem? Data { get; set; }
[Parameter]
public GridOptions? Options { get; set; }
}
Ne feledje azonban, hogy a primitív paraméterek osztályba való összekapcsolása nem mindig jelent előnyt. Bár csökkentheti a paraméterek számát, a változásészlelés és a renderelés viselkedésére is hatással van. A nem primitív paraméterek átadása mindig újra renderelést vált ki, mert Blazor nem tudja, hogy az tetszőleges objektumok belsőleg mutable állapotban vannak-e, míg a primitív paraméterek átadása csak akkor aktivál újra renderelést, ha az értékek ténylegesen megváltoztak.
Vegye figyelembe azt is, hogy jobb lehet, ha nincs táblázatcella-összetevője, és ehelyett beilleszti a logikáját a szülőösszetevőbe.
Megjegyzés:
Ha több megközelítés érhető el a teljesítmény javítása érdekében, általában a megközelítések teljesítményértékelésére van szükség annak meghatározásához, hogy melyik megközelítés a legjobb eredményt adja.
Az általános típusparaméterekkel (@typeparam) kapcsolatos további információkért tekintse meg a következő erőforrásokat:
- Razor ASP.NET Core szintaxisának referenciája
- ASP.NET Core Razor-összetevők
- ASP.NET Core Blazor sablonalapú összetevők
A kaszkádolt paraméterek rögzítettségének ellenőrzése
A CascadingValue összetevő opcionális IsFixed paramétert tartalmaz:
- Ha
IsFixedfalse(alapértelmezett), a kaszkádolt érték minden címzettje beállít egy előfizetést a változásértesítések fogadásához. Mindenlényegesen drágább , mint egy normál az előfizetés nyomon követése miatt. - Ha
IsFixedtrue(például<CascadingValue Value="someValue" IsFixed="true">), a címzettek megkapják a kezdeti értéket, de nem állítanak be előfizetést a frissítések fogadására. Minden[CascadingParameter]könnyű és nem drágább, mint egy hagyományos[Parameter].
A IsFixedtrue beállítása javítja a teljesítményt, ha sok más összetevő kapja a kaszkádolt értéket. Ahol csak lehetséges, állítsa be a IsFixed értékét true-re a kaszkádolt értékeknél. Beállíthatja IsFixedtrue, ha a megadott érték nem változik az idő múlásával.
Ha egy összetevő kaszkádolt értékként adja tovább this-t, IsFixed-et is be lehet állítani true-re, mivel this soha nem változik az összetevő életciklusa során.
<CascadingValue Value="this" IsFixed="true">
<SomeOtherComponents>
</CascadingValue>
További információ: ASP.NET Core Blazor kaszkádolt értékek és paraméterek.
Kerülje az attribútumszórást a CaptureUnmatchedValues-val
Az összetevők választhatják a "nem egyező" paraméterértékek fogadását a CaptureUnmatchedValues jelző használatával:
<div @attributes="OtherAttributes">...</div>
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? OtherAttributes { get; set; }
}
Ez a megközelítés lehetővé teszi tetszőleges további attribútumok átadását az elemnek. Ez a megközelítés azonban költséges, mert a renderelőnek a következőt kell tennie:
- Hasonlítsa össze az összes megadott paramétert az ismert paraméterek készletével, hogy létrehozzon egy szótárt.
- Nyomon követheti, hogy ugyanazon attribútum több példánya hogyan írja felül egymást.
Olyan CaptureUnmatchedValues használjon, ahol az összetevők renderelési teljesítménye nem kritikus, például olyan összetevőket, amelyek nem ismétlődnek gyakran. A nagy léptékben renderelt összetevőknél, például egy nagy listában vagy egy rács celláiban lévő elemeknél próbálja meg elkerülni az attribútumok splatizálását.
További információkért lásd: ASP.NET Core Blazor attribútumszórás és tetszőleges paraméterek.
SetParametersAsync manuális implementálása
Az összetevőnkénti renderelési többletterhelés jelentős forrása a bejövő paraméterértékek írása [Parameter] tulajdonságokba. A renderelő a reflexió-t használja a paraméterértékek megírásához, ami nagy skálán gyenge teljesítményhez vezethet.
Bizonyos szélsőséges esetekben érdemes lehet elkerülni a tükrözést, és manuálisan implementálni a saját paraméterbeállítási logikáját. Ez akkor alkalmazható, ha:
- Egy összetevő rendkívül gyakran jelenik meg, például akkor, ha az összetevő több száz vagy több ezer példányban jelenik meg a felhasználói felületen.
- Egy összetevő számos paramétert fogad el.
- Azt tapasztalja, hogy a paraméterek fogadásának többletterhelése megfigyelhető hatással van a felhasználói felület válaszkészségére.
Szélsőséges esetekben felülbírálhatja az összetevő virtuális SetParametersAsync metódusát, és implementálhatja saját összetevőspecifikus logikáját. Az alábbi példa szándékosan kerüli a szótárkereséseket:
@code {
[Parameter]
public int MessageId { get; set; }
[Parameter]
public string? Text { get; set; }
[Parameter]
public EventCallback<string> TextChanged { get; set; }
[Parameter]
public Theme CurrentTheme { get; set; }
public override Task SetParametersAsync(ParameterView parameters)
{
foreach (var parameter in parameters)
{
switch (parameter.Name)
{
case nameof(MessageId):
MessageId = (int)parameter.Value;
break;
case nameof(Text):
Text = (string)parameter.Value;
break;
case nameof(TextChanged):
TextChanged = (EventCallback<string>)parameter.Value;
break;
case nameof(CurrentTheme):
CurrentTheme = (Theme)parameter.Value;
break;
default:
throw new ArgumentException($"Unknown parameter: {parameter.Name}");
}
}
return base.SetParametersAsync(ParameterView.Empty);
}
}
Az előző kódban az alaposztály visszaadása SetParametersAsync a normál életciklus-metódust futtatja paraméterek ismételt hozzárendelése nélkül.
Ahogy az előző kódban is látható, az SetParametersAsync felülírása és az egyéni logika megadása bonyolult és nehéz feladat, ezért általában nem javasoljuk ennek a megközelítésnek a bevezetését. Szélsőséges esetekben 20–25%javíthatja a renderelési teljesítményt, de ezt a megközelítést csak az ebben a szakaszban korábban felsorolt szélsőséges forgatókönyvekben érdemes figyelembe venni.
Ne aktiválja túl gyorsan az eseményeket
Egyes böngészőesemények rendkívül gyakran történnek. Például a onmousemove és a onscroll másodpercenként több tízszer vagy százszor is aktiválódhatnak. A legtöbb esetben nem kell ilyen gyakran felhasználói felületi frissítéseket végrehajtania. Ha az események túl gyorsan aktiválódnak, károsíthatja a felhasználói felület válaszkészségét, vagy túlzott processzoridőt használhat fel.
Ahelyett, hogy gyorsan aktiválódó natív eseményeket használnál, fontolja meg a JS interop használatát egy ritkábban aktiválódó visszahívás regisztrálásához. Az alábbi összetevő például az egér pozícióját jeleníti meg, de legfeljebb 500 ms-ként frissül:
@implements IDisposable
@inject IJSRuntime JS
<h1>@message</h1>
<div @ref="mouseMoveElement" style="border:1px dashed red;height:200px;">
Move mouse here
</div>
@code {
private ElementReference mouseMoveElement;
private DotNetObjectReference<MyComponent>? selfReference;
private string message = "Move the mouse in the box";
[JSInvokable]
public void HandleMouseMove(int x, int y)
{
message = $"Mouse move at {x}, {y}";
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
selfReference = DotNetObjectReference.Create(this);
var minInterval = 500;
await JS.InvokeVoidAsync("onThrottledMouseMove",
mouseMoveElement, selfReference, minInterval);
}
}
public void Dispose() => selfReference?.Dispose();
}
A megfelelő JavaScript-kód regisztrálja a DOM eseményfigyelőt az egér mozgásához. Ebben a példában az eseményfigyelő Lodash throttle függvényét használja a meghívások sebességének korlátozásához:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
<script>
function onThrottledMouseMove(elem, component, interval) {
elem.addEventListener('mousemove', _.throttle(e => {
component.invokeMethodAsync('HandleMouseMove', e.offsetX, e.offsetY);
}, interval));
}
</script>
Kerülje az újrarendezést az állapotváltozások nélküli események kezelése után
Az összetevők a ComponentBase-ból öröklik, amely automatikusan meghívja a StateHasChanged-et azután, hogy az összetevő eseménykezelőit meghívták. Bizonyos esetekben előfordulhat, hogy az eseménykezelő meghívása után szükségtelen vagy nem kívánatos újrarenderelést elindítani. Előfordulhat például, hogy egy eseménykezelő nem módosítja az összetevő állapotát. Ezekben a forgatókönyvekben az alkalmazás a IHandleEvent felület használatával szabályozhatja Blazoreseménykezelésének viselkedését.
Megjegyzés:
A jelen szakaszban ismertetett megközelítés nem engedi, hogy a kivételek elérjék a hibahatárokat. További információk és a hibahatárokat a ComponentBase.DispatchExceptionAsyncmeghívásával támogató bemutató kódért lásd: AsNonRenderingEventHandler + ErrorBoundary = nem várt viselkedés (dotnet/aspnetcore #54543).
Ha meg szeretné akadályozni az összetevők eseménykezelőinek átrendezését, implementáljon IHandleEvent, és adjon meg egy IHandleEvent.HandleEventAsync feladatot, amely meghívja az eseménykezelőt StateHasChangedmeghívása nélkül.
Az alábbi példában nincs olyan eseménykezelő hozzáadva az összetevőhöz, amely újra-renderelést okozna, ezért a HandleSelect nem eredményez újra-renderelést, amikor meghívásakor.
HandleSelect1.razor:
@page "/handle-select-1"
@using Microsoft.Extensions.Logging
@implements IHandleEvent
@inject ILogger<HandleSelect1> Logger
<p>
Last render DateTime: @dt
</p>
<button @onclick="HandleSelect">
Select me (Avoids Rerender)
</button>
@code {
private DateTime dt = DateTime.Now;
private void HandleSelect()
{
dt = DateTime.Now;
Logger.LogInformation("This event handler doesn't trigger a rerender.");
}
Task IHandleEvent.HandleEventAsync(
EventCallbackWorkItem callback, object? arg) => callback.InvokeAsync(arg);
}
Annak érdekében, hogy megelőzzük a komponens eseménykezelőire történő újrarendereléseket globális módon, lehetőség van arra is, hogy egyetlen eseménykezelő után elkerüljük az újrarendereléseket az alábbi segédprogram használatával.
Adja hozzá a következő EventUtil osztályt egy Blazor alkalmazáshoz. A EventUtil osztály tetején található statikus műveletek és függvények olyan kezelőket biztosítanak, amelyek számos argumentum-kombinációt fednek le, és olyan típusokat adnak vissza, amelyeket Blazor használnak az események kezelésekor.
EventUtil.cs:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
public static class EventUtil
{
public static Action AsNonRenderingEventHandler(Action callback)
=> new SyncReceiver(callback).Invoke;
public static Action<TValue> AsNonRenderingEventHandler<TValue>(
Action<TValue> callback)
=> new SyncReceiver<TValue>(callback).Invoke;
public static Func<Task> AsNonRenderingEventHandler(Func<Task> callback)
=> new AsyncReceiver(callback).Invoke;
public static Func<TValue, Task> AsNonRenderingEventHandler<TValue>(
Func<TValue, Task> callback)
=> new AsyncReceiver<TValue>(callback).Invoke;
private record SyncReceiver(Action callback)
: ReceiverBase { public void Invoke() => callback(); }
private record SyncReceiver<T>(Action<T> callback)
: ReceiverBase { public void Invoke(T arg) => callback(arg); }
private record AsyncReceiver(Func<Task> callback)
: ReceiverBase { public Task Invoke() => callback(); }
private record AsyncReceiver<T>(Func<T, Task> callback)
: ReceiverBase { public Task Invoke(T arg) => callback(arg); }
private record ReceiverBase : IHandleEvent
{
public Task HandleEventAsync(EventCallbackWorkItem item, object arg) =>
item.InvokeAsync(arg);
}
}
Hívja meg a EventUtil.AsNonRenderingEventHandler egy olyan eseménykezelő meghívásához, amely meghíváskor nem indít el renderelést.
Az alábbi példában:
- Ha az első gombot választja, amely meghívja
HandleClick1-t, újrarenderelést indít el. - Ha a második gombot választja, amely meghívja a(z)
HandleClick2-t, nem indít újrarenderelést. - Ha a harmadik gombot választja, amely meghívja
HandleClick3, az nem indítja el az újrarenderelést, és eseményargumentumokat használ (MouseEventArgs).
HandleSelect2.razor:
@page "/handle-select-2"
@using Microsoft.Extensions.Logging
@inject ILogger<HandleSelect2> Logger
<p>
Last render DateTime: @dt
</p>
<button @onclick="HandleClick1">
Select me (Rerenders)
</button>
<button @onclick="EventUtil.AsNonRenderingEventHandler(HandleClick2)">
Select me (Avoids Rerender)
</button>
<button @onclick="EventUtil.AsNonRenderingEventHandler<MouseEventArgs>(HandleClick3)">
Select me (Avoids Rerender and uses <code>MouseEventArgs</code>)
</button>
@code {
private DateTime dt = DateTime.Now;
private void HandleClick1()
{
dt = DateTime.Now;
Logger.LogInformation("This event handler triggers a rerender.");
}
private void HandleClick2()
{
dt = DateTime.Now;
Logger.LogInformation("This event handler doesn't trigger a rerender.");
}
private void HandleClick3(MouseEventArgs args)
{
dt = DateTime.Now;
Logger.LogInformation(
"This event handler doesn't trigger a rerender. " +
"Mouse coordinates: {ScreenX}:{ScreenY}",
args.ScreenX, args.ScreenY);
}
}
A IHandleEvent felület implementálása mellett a cikkben ismertetett egyéb ajánlott eljárások alkalmazása is segíthet csökkenteni a nemkívánatos rendereléseket az események kezelése után. Például a célösszetevő gyermekösszetevőiben a ShouldRender felülbírálása használható az újrarenderelés irányítására.
Kerülje a delegáltak újrateremtését sok ismétlődő elem vagy összetevő esetén.
Blazor"lambda kifejezés delegáltjainak a ciklus elemeire vagy összetevőire való újra létrehozása gyenge teljesítményt eredményezhet.
A eseménykezelési cikkben látható alábbi összetevő gombkészletet jelenít meg. Minden gomb hozzárendel egy delegáltat a @onclick eseményhez, ami akkor jó, ha nem sok gombot kell megjeleníteni.
EventHandlerExample5.razor:
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
@page "/event-handler-example-5"
<h1>@heading</h1>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<p>
<button @onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}
}
Ha az előző megközelítéssel nagyszámú gomb jelenik meg, a renderelési sebesség hátrányosan befolyásolja a megjelenítési sebességet, ami rossz felhasználói élményt eredményez. Nagy számú gomb megjelenítéséhez kattintási események visszahívásával az alábbi példa gombobjektumok gyűjteményét használja fel, amelyek az egyes gombok @onclick delegáltját rendelik az Action-hez. Az alábbi megközelítés nem követeli meg, hogy Blazor újraépítse az összes gombmeghatalmazást minden egyes gombmegjelenítéskor:
LambdaEventPerformance.razor:
@page "/lambda-event-performance"
<h1>@heading</h1>
@foreach (var button in Buttons)
{
<p>
<button @key="button.Id" @onclick="button.Action">
Button #@button.Id
</button>
</p>
}
@code {
private string heading = "Select a button to learn its position";
private List<Button> Buttons { get; set; } = new();
protected override void OnInitialized()
{
for (var i = 0; i < 100; i++)
{
var button = new Button();
button.Id = Guid.NewGuid().ToString();
button.Action = (e) =>
{
UpdateHeading(button, e);
};
Buttons.Add(button);
}
}
private void UpdateHeading(Button button, MouseEventArgs e)
{
heading = $"Selected #{button.Id} at {e.ClientX}:{e.ClientY}";
}
private class Button
{
public string? Id { get; set; }
public Action<MouseEventArgs> Action { get; set; } = e => { };
}
}