Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Výstraha
Tato verze ASP.NET Core již není podporována. Pro více informací se podívejte na Zásady podpory .NET a .NET Core. Aktuální vydání tohoto článku najdete v verzi .NET 9.
Optimalizujte rychlost vykreslování, abyste minimalizovali úlohy vykreslování a zlepšili rychlost odezvy uživatelského rozhraní, což může přinést desetinásobné nebo vyšší vylepšení rychlosti vykreslování uživatelského rozhraní.
Vyhněte se zbytečnému vykreslování podstromů součástí
Většinu nákladů na vykreslování nadřazené komponenty můžete odebrat tak, že při výskytu události přeskočíte překočení podřízených podstromů komponent. Měli byste se zabývat pouze vynecháváním podstromů rerenderingu, které jsou zvlášť nákladné k vykreslení a způsobují prodlevu uživatelského rozhraní.
Za běhu existují komponenty v hierarchii. Kořenová komponenta (první načtená komponenta) obsahuje podřízené komponenty. Naopak děti kořene mají své vlastní podřízené komponenty a tak dále. Když dojde k události, jako je například uživatel, který vybere tlačítko, určuje následující proces, které součásti se mají znovu vyřazuje:
- Událost je odeslána do komponenty, která vykreslovala obslužnou rutinu události. Po spuštění obslužné rutiny události se komponenta znovu vyenderuje.
- Když je komponenta znovu vysílaná, poskytuje nové kopii hodnot parametrů pro každou z jejích podřízených komponent.
- Po přijetí nové sady hodnot parametrů rozhoduje Blazor, zda se má komponenta znovu vykreslit. Components rerender if
ShouldRenderreturnstrue, což je výchozí chování, pokud není přepsáno, a hodnoty parametrů se mohou změnit, například pokud jsou proměnlivé objekty.
Poslední dva kroky předchozí sekvence pokračují rekurzivně dolů v hierarchii komponent. V mnoha případech je celý podstrom překreslován. Události, které cílí na komponenty vysoké úrovně, můžou způsobit nákladné opětovnéenderování, protože každá komponenta pod komponentou vysoké úrovně musí znovu enderovat.
Pokud chcete zabránit rekurzi vykreslování do určitého podstromu, použijte některý z následujících přístupů:
- Ujistěte se, že parametry podřízené komponenty jsou konkrétními neměnnými typy, jako například
string,int,boolaDateTime. Integrovaná logika pro detekci změn automaticky přeskočí rerendering, pokud se nezměnily neměnné hodnoty parametrů. Pokud vykreslujete podřízenou komponentu s typem<Customer CustomerId="item.CustomerId" />CustomerIdint, není komponenta znovu vygenerována,Customerpokuditem.CustomerIdse nezmění. - Přepsání
ShouldRender, vrácenífalse:- Pokud jsou parametry neprimitivní typy nebo nepodporované neměnné typy†, jako jsou komplexní vlastní typy modelu nebo hodnoty RenderFragment, a hodnoty parametrů se nezměnily,
- Pokud vytváření komponenty jen pro uživatelské rozhraní, která se po počátečním vykreslení nezmění, bez ohledu na změny hodnoty parametru.
† Další informace najdete v logice detekce změn v Blazorreferenčním zdroji (ChangeDetection.cs).
Poznámka:
Odkazy na referenční zdroj .NET v dokumentaci obvykle otevřou výchozí větev úložiště, která představuje aktuální vývoj další verze .NET. Pokud chcete vybrat značku pro konkrétní verzi, použijte rozevírací seznam pro přepnutí mezi větvemi nebo značkami. Další informace najdete v tématu Jak vybrat značku verze zdrojového kódu ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Následující příklad vyhledávacího nástroje letecké společnosti používá soukromá pole ke sledování potřebných informací ke zjištění změn. Předchozí identifikátor příchozích letů (prevInboundFlightId) a předchozí identifikátor odchozích letů (prevOutboundFlightId) sledují informace pro další možnou aktualizaci komponent. Pokud se některý z identifikátorů letu změní, když jsou parametry komponenty nastaveny , OnParametersSetkomponenta je znovu vysunuta, protože shouldRender je nastavena na true. Pokud shouldRender se vyhodnotí jako false po kontrole identifikátorů letu, vyhnete se nákladnému rerenderu:
@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;
}
Obslužná rutina události může být také nastavena shouldRender na true. U většiny komponent obvykle není potřeba určit rerendering na úrovni jednotlivých obslužných rutin událostí.
Další informace najdete v následujících zdrojích informací:
- životní cyklus komponent ASP.NET Core Razor
- ShouldRender
- Vykreslování komponent ASP.NET Core Razor
Virtualizace
Při vykreslování velkých objemů uživatelského rozhraní ve smyčce, například v seznamu nebo mřížce s tisíci položek, může při vykreslování uživatelského rozhraní dojít k prodlevě. Vzhledem k tomu, že uživatel vidí jenom malý počet prvků najednou bez posouvání, je často plýtvání časem vykreslování prvků, které nejsou aktuálně viditelné.
Blazor poskytuje komponentu Virtualize<TItem> pro vytvoření vzhledu a posouvání chování libovolného velkého seznamu při vykreslování pouze položek seznamu, které jsou v aktuálním zobrazení pro posouvání. Komponenta může například vykreslit seznam s 100 000 položkami, ale platí jenom náklady na vykreslování 20 položek, které jsou viditelné.
Další informace najdete v tématu virtualizace komponent ASP.NET CoreRazor.
Vytváření jednoduchých a optimalizovaných komponent
Většina Razor komponent nevyžaduje agresivní optimalizaci, protože většina komponent se v uživatelském rozhraní neopakuje a neopakuje se s vysokou frekvencí. Například směrovatelné komponenty se direktivou @page a komponentami, které se používají k vykreslení částí uživatelského rozhraní vysoké úrovně, jako jsou dialogy nebo formuláře, se s největší pravděpodobností zobrazují jenom jednou najednou a v reakci na gesto uživatele se zobrazí pouze jednou. Tyto komponenty obvykle nevytvoří vysokou úlohu vykreslování, takže můžete volně používat libovolnou kombinaci funkcí architektury, aniž byste se museli zabývat výkonem vykreslování.
Existují však běžné scénáře, kdy se komponenty opakují ve velkém měřítku a často vedou k nízkému výkonu uživatelského rozhraní:
- Velké vnořené formy se stovkami jednotlivých prvků, jako jsou vstupy nebo popisky.
- Mřížky se stovkami řádků nebo tisíci buněk
- Bodové grafy s miliony datových bodů
Pokud modeluje každý prvek, buňku nebo datový bod jako samostatnou instanci komponenty, často jich existuje tolik, aby jejich výkon vykreslování byl kritický. Tato část obsahuje rady k tomu, aby tyto komponenty byly jednoduché, aby uživatelské rozhraní zůstalo rychlé a responzivní.
Vyhněte se tisícům instancí komponent
Každá komponenta je samostatný ostrov, který se může vykreslit nezávisle na svých nadřazených a podřízených objektech. Výběrem způsobu rozdělení uživatelského rozhraní do hierarchie komponent přebíráte kontrolu nad členitostí vykreslování uživatelského rozhraní. Výsledkem může být dobrý nebo nízký výkon.
Rozdělením uživatelského rozhraní do samostatných komponent můžete mít menší části rerenderu uživatelského rozhraní, když dojde k událostem. V tabulce s mnoha řádky, které mají na každém řádku tlačítko, může být možné, že budete moct znovu vyřadit pouze jeden řádek pomocí podřízené komponenty místo celé stránky nebo tabulky. Každá komponenta ale vyžaduje další režii paměti a procesoru, aby se vyřešil její nezávislý stav a životní cyklus vykreslování.
V testu prováděném techniky jednotek produktu ASP.NET Core se v Blazor WebAssembly aplikaci zobrazila režie vykreslování přibližně 0,06 ms na instanci komponenty. Testovací aplikace vykreslovala jednoduchou komponentu, která přijímá tři parametry. Režie je z velké části způsobená načtením stavu jednotlivých součástí ze slovníků a předáváním a příjmem parametrů. Pomocí násobení můžete vidět, že přidání 2 000 dalších instancí součástí by do doby vykreslování přidalo 0,12 sekundy a uživatelské rozhraní by uživatelům začalo být pomalé.
Komponenty je možné usnadnit, abyste jich mohli mít více. Výkonnější technika je ale často, abyste se vyhnuli tolika komponentám, které se mají vykreslit. Následující části popisují dva přístupy, které můžete provést.
Další informace o správě paměti najdete v tématu Správa paměti v nasazených aplikacích na straně Blazor serveru ASP.NET Core.
Vložení podřízených komponentů do rodičovských: Uvažte následující část rodičovské komponenty, která vykresluje podřízené komponenty ve smyčce:
<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; }
}
Předchozí příklad funguje dobře, pokud se najednou nezobrazují tisíce zpráv. Pokud chcete zobrazit tisíce zpráv najednou, zvažte nefaktoring samostatné ChatMessageDisplay komponenty. Místo toho vložte podřízenou komponentu do nadřazeného objektu. Následující přístup zabraňuje režijním nákladům na jednotlivé součásti vykreslování tak velkého počtu podřízených komponent za cenu ztráty schopnosti rerenderovat značky jednotlivých podřízených komponent nezávisle:
<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>
Definování opakovaně použitelného RenderFragments v kódu: Podřízené komponenty můžete použít čistě jako způsob opakovaného použití logiky vykreslování. V takovém případě můžete vytvořit opakovaně použitelnou logiku vykreslování bez implementace dalších komponent. V bloku libovolné komponenty @code definujte .RenderFragment Vykreslí fragment z libovolného umístění tolikrát, kolikrát potřebujete:
@RenderWelcomeInfo
<p>Render the welcome content a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = @<p>Welcome to your new app!</p>;
}
Pokud chcete, aby RenderTreeBuilder byl kód opakovaně použitelný napříč více komponentami, deklarujte RenderFragmentpublicstatic
public static RenderFragment SayHello = @<h1>Hello!</h1>;
SayHello v předchozím příkladu lze vyvolat z nesouvisející komponenty. Tato technika je užitečná pro vytváření knihoven opakovaně použitelných fragmentů značek, které se vykreslují bez režie na jednotlivé komponenty.
RenderFragment delegáti mohou přijímat parametry. Následující komponenta předá delegátu message zprávu (RenderFragment):
<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>;
}
Předchozí přístup opakovaně používá logiku vykreslování bez režie na jednotlivé součásti. Přístup ale neumožňuje nezávisle aktualizovat podstrom uživatelského rozhraní ani nemá možnost přeskočit vykreslování podstromu uživatelského rozhraní, když se jeho nadřazený objekt vykresluje, protože neexistuje žádná hranice komponent. Přiřazení delegáta RenderFragment je podporováno pouze v souborech komponent Razor (.razor).
Pro nestatické pole, metodu nebo vlastnost, na kterou nelze odkazovat inicializátorem pole, například TitleTemplate v následujícím příkladu, použijte vlastnost místo pole pro RenderFragment:
protected RenderFragment DisplayTitle =>
@<div>
@TitleTemplate
</div>;
Nepřibírat příliš mnoho parametrů
Pokud se komponenta opakuje velmi často, například stovky nebo tisícekrát, režie při předávání a přijímání jednotlivých parametrů se sestaví.
Je vzácné, že příliš mnoho parametrů výrazně omezuje výkon, ale může to být faktor.
TableCell U komponenty, která v mřížce vykresluje 4 000krát, každý parametr předaný komponentě přidá do celkových nákladů na vykreslování přibližně 15 ms. Předání deseti parametrů vyžaduje přibližně 150 ms a způsobuje prodlevu vykreslování uživatelského rozhraní.
Pokud chcete snížit zatížení parametrů, sbalte několik parametrů do vlastní třídy. Například komponenta buňky tabulky může přijmout společný objekt. V následujícím příkladu Data se pro každou buňku liší, ale Options je společný pro všechny instance buněk:
@typeparam TItem
...
@code {
[Parameter]
public TItem? Data { get; set; }
[Parameter]
public GridOptions? Options { get; set; }
}
Mějte ale na paměti, že sdružování primitivních parametrů do třídy není vždy výhodou. I když může snížit počet parametrů, ovlivňuje také chování detekce a vykreslování změn. Předávání nepřimitivitních parametrů vždy aktivuje opětovné vykreslení, protože Blazor nemůže zjistit, jestli mají libovolné objekty interně proměnlivý stav, zatímco předávání primitivních parametrů aktivuje opětovné vykreslení pouze v případě, že se jejich hodnoty skutečně změnily.
Vezměte také v úvahu, že by mohlo být výhodnější nemít samostatnou součást buňky tabulky, jak ukazuje předchozí příklad, a místo toho začlenit její logiku přímo do nadřazené komponenty.
Poznámka:
Pokud je k dispozici více přístupů pro zlepšení výkonu, je obvykle potřeba provést srovnávací testy, aby bylo možné určit, který přístup poskytuje nejlepší výsledky.
Další informace o parametrech obecného typu (@typeparam) najdete v následujících zdrojích informací:
- Referenční informace k syntaxi Razor pro ASP.NET Core
- komponenty ASP.NET Core Razor
- Šablonové komponenty ASP.NET CoreBlazor
Ujistěte se, že jsou opravené kaskádové parametry.
Komponenta CascadingValue
- Pokud
IsFixedje (výchozí),falsekaždý příjemce kaskádové hodnoty nastaví odběr pro příjem oznámení o změnách. Každá z nich[CascadingParameter]je podstatně dražšísledování předplatného. - Pokud
IsFixedjetrue(například<CascadingValue Value="someValue" IsFixed="true">), příjemci obdrží počáteční hodnotu, ale nenastaví předplatné pro příjem aktualizací. Každý[CascadingParameter]je jednoduchý a není dražší než normální[Parameter].
Nastavení IsFixed pro true zvýšení výkonu, pokud existuje velký počet dalších komponent, které přijímají kaskádovou hodnotu. Pokud je to možné, nastavte IsFixed na true kaskádové hodnoty. Můžete nastavit IsFixedtrue , když se zadaná hodnota v průběhu času nezmění.
Pokud komponenta předává this kaskádovou hodnotou, lze IsFixed nastavit také na true, protože this se během životního cyklu komponenty nikdy nemění:
<CascadingValue Value="this" IsFixed="true">
<SomeOtherComponents>
</CascadingValue>
Další informace najdete v tématu ASP.NET Core Blazor kaskádové hodnoty a parametry.
Vyhněte se dělení atributů pomocí CaptureUnmatchedValues
Komponenty se můžou rozhodnout přijímat hodnoty parametrů bez neshod pomocí příznaku CaptureUnmatchedValues :
<div @attributes="OtherAttributes">...</div>
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? OtherAttributes { get; set; }
}
Tento přístup umožňuje předat elementu libovolné další atributy. Tento přístup je ale nákladný, protože renderer musí:
- Porovná všechny zadané parametry se sadou známých parametrů pro sestavení slovníku.
- Sledujte, jak se vzájemně přepisuje více kopií stejného atributu.
Použijte CaptureUnmatchedValues , kde výkon vykreslování součástí není kritický, například komponenty, které se často nepoužívají. U komponent, které se vykreslují ve velkém měřítku, například u každé položky ve velkém seznamu nebo v buňkách mřížky, se snažte vyhnout dělení atributů.
Další informace najdete v tématu
Ruční implementace SetParametersAsync
Významným zdrojem režie vykreslování jednotlivých součástí je zápis příchozích hodnot parametrů do [Parameter] vlastností. Renderer používá reflexi k zápisu hodnot parametrů, což může vést k nízkému výkonu ve velkém měřítku.
V některých extrémních případech se můžete chtít vyhnout reflexi a implementovat vlastní logiku nastavení parametrů ručně. To se může použít v těchto případech:
- Komponenta se vykreslí velmi často, například když v uživatelském rozhraní existují stovky nebo tisíce kopií komponenty.
- Komponenta přijímá mnoho parametrů.
- Zjistíte, že režie při příjmu parametrů má pozorovatelný dopad na odezvu uživatelského rozhraní.
Vextrémních SetParametersAsync Následující příklad záměrně zabraňuje vyhledávání slovníku:
@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);
}
}
Vrácení základní třídy SetParametersAsync v předchozím kódu spustí normální metodu životního cyklu bez opětovného přiřazení parametrů.
Jak vidíte v předchozím kódu, přepsání SetParametersAsync a dodání vlastní logiky je složité a pracné, takže obecně nedoporučujeme tento přístup používat. V extrémních případech může zvýšit výkon vykreslování o 20 až 25 %, ale tento přístup byste měli zvážit pouze v extrémních scénářích uvedených dříve v této části.
Neaktivujte události příliš rychle
Některé události prohlížeče se aktivuje velmi často. Může například onmousemoveonscroll střílet desítky nebo stovkykrát za sekundu. Ve většině případů nemusíte provádět aktualizace uživatelského rozhraní často. Pokud se události aktivují příliš rychle, můžete poškodit odezvu uživatelského rozhraní nebo spotřebovat nadměrnou dobu procesoru.
Místo použití nativních událostí, které se rychle aktivují, zvažte použití zprostředkovatele komunikace k registraci zpětného JS volání, které se aktivuje méně často. Například následující komponenta zobrazí pozici myši, ale aktualizuje se maximálně jednou každých 500 ms:
@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();
}
Odpovídající kód JavaScriptu zaregistruje naslouchací proces událostí MODELU DOM pro pohyb myši. V tomto příkladu naslouchací proces událostí používá throttle Lodash k omezení frekvence volání:
<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>
Vyhněte se opakovanémuenderování po zpracování událostí bez změn stavu.
Komponenty dědí z ComponentBaseobjektu , který se automaticky vyvolá StateHasChanged po vyvolání obslužných rutin událostí komponenty. V některých případech může být zbytečné nebo nežádoucí aktivovat opětovné spuštění po vyvolání obslužné rutiny události. Obslužná rutina události například nemusí změnit stav komponenty. V těchto scénářích může aplikace využít IHandleEvent rozhraní k řízení chování Blazorzpracování událostí.
Poznámka:
Přístup v této části neprovádí výjimky z chybových hranic. Další informace a demonstrační kód, který podporuje hranice chyb voláním ComponentBase.DispatchExceptionAsync, naleznete v tématu AsNonRenderingEventHandler + ErrorBoundary = neočekávané chování (dotnet/aspnetcore #54543).
Chcete-li zabránit rerenders pro všechny obslužné rutiny událostí komponenty, implementujte IHandleEvent a poskytněte IHandleEvent.HandleEventAsync úlohu, která vyvolá obslužnou rutinu události bez volání StateHasChanged.
V následujícím příkladu žádná obslužná rutina události přidaná do komponenty neaktivuje rerender, takže HandleSelect při vyvolání nedojde k opětovnému rerenderu.
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);
}
Kromě toho, aby se zabránilo opětovnémuenderování po vyvolání obslužných rutin událostí v komponentě globálním způsobem, je možné zabránit opětovnémuenderu po jedné obslužné rutině události pomocí následující metody utility.
Přidejte do EventUtil aplikace následující Blazor třídu. Statické akce a funkce v horní části EventUtil třídy poskytují obslužné rutiny, které pokrývají několik kombinací argumentů a návratových typů, které Blazor se používají při zpracování událostí.
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);
}
}
Volání EventUtil.AsNonRenderingEventHandler volání obslužné rutiny události, která při vyvolání neaktivuje vykreslení.
V následujícím příkladu:
- Výběr prvního tlačítka, které volá
HandleClick1, aktivuje rerender. - Když vyberete druhé tlačítko, které volá
HandleClick2, neaktivuje se rerender. - Výběr třetího tlačítka, které volá
HandleClick3, neaktivuje rerender a používá argumenty události (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);
}
}
Kromě implementace IHandleEvent rozhraní může využití dalších osvědčených postupů popsaných v tomto článku také pomoct snížit nežádoucí vykreslení po zpracování událostí. Například přepsání ShouldRender v podřízených součástech cílové komponenty lze použít k řízení rerenderingu.
Vyhněte se opětovnému vytváření delegátů pro mnoho opakovaných prvků nebo součástí.
BlazorRekreace výrazů lambda delegátů pro prvky nebo komponenty ve smyčce může vést k nízkému výkonu.
Následující komponenta zobrazená v článku o zpracování událostí vykreslí sadu tlačítek. Každé tlačítko přiřadí delegáta ke své @onclick události, což je v pořádku, pokud není k dispozici mnoho tlačítek k vykreslení.
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}";
}
}
Pokud se pomocí předchozího přístupu vykresluje velký počet tlačítek, má to nepříznivý dopad na rychlost vykreslování, což vede ke špatnému uživatelskému prostředí. Chcete-li vykreslit velký počet tlačítek s zpětným voláním pro události kliknutí, následující příklad používá kolekci objektů tlačítek, které přiřazují delegáta @onclick každého tlačítka .Action Následující přístup nevyžaduje Blazor opětovné sestavení všech delegátů tlačítka při každém vykreslení tlačítek:
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 => { };
}
}