Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 10 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la política de soporte de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Optimice la velocidad de representación para minimizar la carga de trabajo de representación y mejorar la capacidad de respuesta de la interfaz de usuario, lo que puede producir una mejora de diez veces o superior en la velocidad de representación de la interfaz de usuario.
Evitar la representación innecesaria de subárboles de componentes
Es posible que pueda eliminar la mayor parte del costo de renderización de un componente principal al omitir el volver a renderizar los subárboles de componentes secundarios cuando se produce un evento. Solo debe preocuparse por omitir los subárboles de renderizado que son especialmente costosos de renderizar y están causando lag en la interfaz de usuario.
En tiempo de ejecución, los componentes existen en una jerarquía. Un componente raíz (el primer componente cargado) tiene componentes secundarios. A su vez, los elementos secundarios de la raíz tienen sus propios subcomponentes, y así sucesivamente. Cuando se produce un evento, como un usuario que selecciona un botón, el siguiente proceso determina qué componentes se van a volver a representar:
- El evento se envía al componente que representa el controlador del evento. Después de ejecutar el controlador de eventos, el componente se vuelve a representar.
- Cuando se vuelve a representar un componente, proporciona una nueva copia de los valores de parámetro a cada uno de sus componentes secundarios.
- Después de recibir un nuevo conjunto de valores de parámetro, Blazor decide si desea volver a representar el componente. Los componentes se vuelven a renderizar si
ShouldRenderdevuelvetrue, que es el comportamiento predeterminado a menos que se invalide. Además, los valores de los parámetros pueden haber cambiado, especialmente si son objetos mutables.
Los dos últimos pasos de la secuencia anterior continúan de forma recursiva hacia abajo en la jerarquía de componentes. En muchos casos, se vuelve a representar todo el subárbol. Los eventos que tienen como destino componentes de alto nivel pueden provocar una nueva representación costosa, ya que todos los componentes debajo del componente de alto nivel deben volver a representarse.
Para evitar la recursividad de representación en un subárbol determinado, use cualquiera de los métodos siguientes:
- Asegúrese de que los parámetros del componente secundario son de tipos inmutables específicos†, como
string,int,boolyDateTime. La lógica integrada para detectar cambios omite automáticamente la representación si los valores de parámetro inmutables no han cambiado. Si renderizas un componente secundario con<Customer CustomerId="item.CustomerId" />, dondeCustomerIdes un tipo deint, entonces el componenteCustomerno se vuelve a renderizar a menos queitem.CustomerIdcambie. - Invalide
ShouldRendery devuelvafalse:- Cuando los parámetros son tipos noprimitivos o tipos inmutables no admitidos†, como los tipos de modelos personalizados complejos o los valores RenderFragment, y los valores de los parámetros no han cambiado,
- Si está creando un componente de solo interfaz de usuario que no cambia después de la representación inicial, independientemente de los cambios en el valor de los parámetros.
†Para obtener más información, consulte la lógica de detección de cambios en Blazorel origen de referencia (ChangeDetection.cs).
Nota:
Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta para una versión específica, use la lista desplegable Cambiar ramas o etiquetas . Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).
En el siguiente ejemplo de la herramienta de búsqueda de vuelos aéreos se usan campos privados para realizar un seguimiento de la información necesaria para detectar cambios. El identificador de vuelo de entrada anterior (prevInboundFlightId) y el identificador de vuelo saliente anterior (prevOutboundFlightId) realizan un seguimiento de la información de la siguiente actualización potencial del componente. Si uno de los identificadores de vuelo cambia cuando los parámetros del componente se establecen en OnParametersSet, el componente se vuelve a renderizar porque shouldRender está configurado en true. Si shouldRender se evalúa como false después de comprobar los identificadores de vuelo, se evita una re-renderización costosa.
@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;
}
Un controlador de eventos también puede establecer shouldRender en true. Para la mayoría de los componentes, determinar el volver a renderizar a nivel de controladores de eventos individuales normalmente no es necesario.
Para obtener más información, consulte los siguientes recursos:
- Ciclo de vida de los componentes Razor de ASP.NET Core
- ShouldRender
- Representación de componentes Razor de ASP.NET Core
Virtualización
Al representar grandes cantidades de interfaz de usuario dentro de un bucle, por ejemplo, una lista o cuadrícula con miles de entradas, la cantidad máxima de operaciones de representación puede provocar un retraso en la representación de la interfaz de usuario. Dado que el usuario solo puede ver un pequeño número de elementos a la vez sin desplazarse, a menudo es un desperdicio dedicar tiempo a renderizar elementos que no están visibles actualmente.
Blazor proporciona el componente Virtualize<TItem> para diseñar la apariencia y el comportamiento de desplazamiento de una lista de tamaño arbitrario, mientras que solo representa los elementos de la lista que están dentro del área visible del desplazamiento actual. Por ejemplo, un componente puede representar una lista con 100 000 entradas, pero solo pagar el costo de representación de 20 elementos visibles.
Para obtener más información, consulta Virtualización de componentes Razor de ASP.NET Core.
Creación de componentes ligeros y optimizados
La mayoría Razor de los componentes no requieren esfuerzos de optimización agresivos porque no se repiten en la interfaz de usuario y no requieren ser renderizados con alta frecuencia. Por ejemplo, los componentes enrutables con una @page directiva y componentes usados para representar elementos de alto nivel de la interfaz de usuario, como cuadros de diálogo o formularios, es probable que solo aparezcan uno a la vez y solo vuelvan a renderizarse en respuesta a un gesto del usuario. Normalmente, estos componentes no crean una carga de trabajo de representación elevada, por lo que puede usar libremente cualquier combinación de características de marco sin preocuparse mucho por el rendimiento de la representación.
Sin embargo, hay escenarios comunes en los que los componentes se repiten a gran escala y suelen dar lugar a un rendimiento deficiente de la interfaz de usuario:
- Formas anidadas grandes con cientos de elementos individuales, como entradas o etiquetas.
- Cuadrículas con cientos de filas o miles de celdas.
- Gráficos de dispersión con millones de puntos de datos.
Si modela cada elemento, celda o punto de datos como una instancia de componente independiente, a menudo hay tantos que su rendimiento de renderizado se vuelve fundamental. En esta sección se proporcionan consejos para que estos componentes sean ligeros para que la interfaz de usuario siga siendo rápida y con capacidad de respuesta.
Evitar miles de instancias de componentes
Cada componente es una isla separada que puede renderizarse independientemente de sus padres e hijos. Al elegir cómo dividir la interfaz de usuario en una jerarquía de componentes, se toma el control sobre la granularidad de la representación de la interfaz de usuario. Esto puede dar lugar a un rendimiento bueno o deficiente.
Al dividir la interfaz de usuario en componentes independientes, puede lograrse que partes más pequeñas de la interfaz de usuario se vuelvan a renderizar cuando se produzcan eventos. En una tabla con muchas filas que tienen un botón en cada fila, puede que solo pueda renderizar esa fila utilizando un componente hijo en lugar de toda la página o tabla. Sin embargo, cada componente requiere un uso adicional de memoria y CPU para gestionar su estado independiente y el ciclo de vida de renderizado.
En una prueba realizada por los ingenieros de unidades de producto de ASP.NET Core, se ha visto una sobrecarga de representación de aproximadamente 0,06 ms por instancia de componente en una Blazor WebAssembly aplicación. La aplicación de prueba ha renderizado un componente sencillo que acepta tres parámetros. Internamente, la sobrecarga se debe en gran medida a la obtención del estado de cada componente de los diccionarios, así como al paso y recepción de parámetros. Mediante la multiplicación, puede ver que agregar 2,000 instancias adicionales de componentes agregaría 0,12 segundos al tiempo de representación y la interfaz de usuario comenzaría a sentirse lenta para los usuarios.
Es posible que los componentes sean más ligeros para que pueda tener más de ellos. Sin embargo, una técnica más eficaz suele evitar tener tantos componentes para representar. En las secciones siguientes se describen dos enfoques que puede adoptar.
Para obtener más información sobre la administración de memoria, consulte Administración de memoria en aplicaciones del lado Blazor servidor ASP.NET Core implementadas.
Componentes secundarios insertados en sus elementos primarios: Tenga en cuenta la siguiente parte de un componente primario que representa los componentes secundarios en un bucle:
<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; }
}
El ejemplo anterior funciona bien si no se muestran miles de mensajes a la vez. Para mostrar miles de mensajes a la vez, considere la posibilidad de no factorizar el componente independiente ChatMessageDisplay . En su lugar, inserte el componente secundario en el elemento primario. El siguiente enfoque evita la sobrecarga por componente de la representación de tantos componentes secundarios a costa de perder la capacidad de volver a representar el marcado de cada componente secundario de forma independiente:
<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>
Definir elementos reutilizables RenderFragments en el código: es posible que esté factorizando subcomponentes exclusivamente como una manera de reutilizar la lógica de renderización. Si es así, puede crear lógica de representación reutilizable sin implementar componentes adicionales. En cualquier componente del bloque @code, defina un RenderFragment. Represente el fragmento desde cualquier ubicación tantas veces como sea necesario:
@RenderWelcomeInfo
<p>Render the welcome content a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = @<p>Welcome to your new app!</p>;
}
Hacer que RenderTreeBuilder el código sea reutilizable en varios componentes, declare RenderFragmentpublic y static:
public static RenderFragment SayHello = @<h1>Hello!</h1>;
SayHello en el ejemplo anterior se puede invocar desde un componente no relacionado. Esta técnica es útil para crear bibliotecas de fragmentos de marcado reutilizables que se representan sin sobrecarga por componente.
RenderFragment los delegados pueden aceptar parámetros. El componente siguiente pasa el mensaje (message) al RenderFragment delegado:
<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>;
}
El enfoque anterior reutiliza la lógica de representación sin sobrecarga por componente. Sin embargo, el enfoque no permite actualizar el subárbol de la UI de forma independiente, ni tiene la capacidad de omitir la renderización del subárbol de la UI cuando su elemento primario se renderiza porque no existe un límite de componente. La asignación a un RenderFragment delegado solo se admite en Razor archivos de componentes (.razor).
Para un campo, método o propiedad no estáticos a los que un inicializador de campo no puede hacer referencia, como TitleTemplate en el ejemplo siguiente, use una propiedad en lugar de un campo para RenderFragment:
protected RenderFragment DisplayTitle =>
@<div>
@TitleTemplate
</div>;
No reciba demasiados parámetros
Si un componente se repite con mucha frecuencia, por ejemplo, cientos o miles de veces, la sobrecarga de pasar y recibir cada parámetro se acumula.
Es raro que demasiados parámetros restrinjan gravemente el rendimiento, pero puede ser un factor. Para un TableCell componente que representa 4000 veces dentro de una cuadrícula, cada parámetro pasado al componente agrega alrededor de 15 ms al costo total de representación. Pasar diez parámetros requiere alrededor de 150 ms y provoca un retraso en la representación de la interfaz de usuario.
Para reducir la carga de parámetros, agrupa varios parámetros en una clase personalizada. Por ejemplo, un componente de celda de tabla podría aceptar un objeto común. En el ejemplo siguiente, Data es diferente para cada celda, pero Options es común en todas las instancias de celda:
@typeparam TItem
...
@code {
[Parameter]
public TItem? Data { get; set; }
[Parameter]
public GridOptions? Options { get; set; }
}
Sin embargo, tenga en cuenta que la agrupación de parámetros primitivos en una clase no siempre es una ventaja. Aunque puede reducir el recuento de parámetros, también afecta al comportamiento de la detección y representación de cambios. Pasar parámetros no primitivos siempre desencadena una nueva representación, ya Blazor que no puede saber si los objetos arbitrarios tienen un estado mutable internamente, mientras que pasar parámetros primitivos solo desencadena una nueva representación si sus valores han cambiado realmente.
Además, tenga en cuenta que podría ser una mejora que no tenga un componente de celda de tabla, como se muestra en el ejemplo anterior y, en su lugar, inserte su lógica en el componente primario.
Nota:
Cuando hay varios enfoques disponibles para mejorar el rendimiento, normalmente se requieren pruebas comparativas de los enfoques para determinar qué enfoque produce los mejores resultados.
Para obtener más información sobre los parámetros de tipo genérico (@typeparam), consulte los siguientes recursos:
- Referencia sobre la sintaxis de Razor para ASP.NET Core
- Componentes Razor de ASP.NET Core
- Componentes con plantilla de Blazor en ASP.NET Core
Asegúrese de que los parámetros en cascada son fijos
El CascadingValue componente tiene un parámetro opcional IsFixed :
- Si
IsFixedesfalse(valor predeterminado), cada destinatario del valor en cascada configura una suscripción para recibir notificaciones de cambio. Cada una[CascadingParameter]es sustancialmente más costosa que una normal[Parameter]debido al seguimiento de suscripciones. - Si
IsFixedestrue(por ejemplo,<CascadingValue Value="someValue" IsFixed="true">), los destinatarios reciben el valor inicial, pero no configuran una suscripción para recibir actualizaciones. Cada uno[CascadingParameter]es ligero y no es más caro que un normal[Parameter].
Establecer IsFixed en true mejora el rendimiento si hay un gran número de otros componentes que reciben el valor en cascada. Siempre que sea posible, asigne IsFixed a true en los valores de cascada. Puede establecer IsFixed en true cuando el valor proporcionado no cambia con el tiempo.
Cuando un componente pasa this como un valor en cascada, IsFixed también se puede establecer en true, porque this nunca cambia durante el ciclo de vida del componente:
<CascadingValue Value="this" IsFixed="true">
<SomeOtherComponents>
</CascadingValue>
Para más información, vea Valores y parámetros en cascada de Blazor en ASP.NET Core.
Evitar el desglose de atributos con CaptureUnmatchedValues
Los componentes pueden optar por recibir valores de parámetro "no coincidentes" mediante la CaptureUnmatchedValues marca :
<div @attributes="OtherAttributes">...</div>
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? OtherAttributes { get; set; }
}
Este enfoque permite pasar atributos adicionales arbitrarios al elemento . Sin embargo, este enfoque es costoso porque el representador debe:
- Comparar todos los parámetros suministrados con el conjunto de parámetros conocidos para construir un diccionario.
- Realice un seguimiento de cómo varias copias del mismo atributo se sobrescriben entre sí.
Use CaptureUnmatchedValues dónde el rendimiento de la representación de componentes no es crítico, como los componentes que no se repiten con frecuencia. En el caso de los componentes que se representan a escala, como cada elemento de una lista grande o en las celdas de una cuadrícula, intente evitar la expansión de atributos.
Para más información, consulte Parámetros arbitrarios y de expansión de atributos de ASP.NET Core Blazor.
Implementación SetParametersAsync manual
Una fuente significativa de sobrecarga de renderizado por componente es escribir valores de parámetro entrantes en los [Parameter] propiedades. El representador usa la reflexión para escribir los valores de parámetro, lo que puede provocar un rendimiento deficiente a escala.
En algunos casos extremos, es posible que desee evitar la reflexión e implementar manualmente su propia lógica de configuración de parámetros. Esto puede ser aplicable cuando:
- Un componente se renderiza con mucha frecuencia, por ejemplo, cuando hay cientos o miles de copias del componente en la UI.
- Un componente acepta muchos parámetros.
- Observa que la sobrecarga de recibir parámetros tiene un impacto observable en la capacidad de respuesta de la interfaz de usuario.
En casos extremos, puede invalidar el método virtual SetParametersAsync del componente e implementar su propia lógica específica del componente. En el ejemplo siguiente se evitan deliberadamente búsquedas de diccionario:
@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);
}
}
En el código anterior, la devolución de la clase SetParametersAsync base ejecuta el método de ciclo de vida normal sin volver a asignar parámetros.
Como puede ver en el código anterior, reemplazar SetParametersAsync y proporcionar lógica personalizada es complicado y laborioso, por lo que generalmente no se recomienda adoptar este enfoque. En casos extremos, puede mejorar el rendimiento de la representación en 20-25%, pero solo debe tener en cuenta este enfoque en los escenarios extremos enumerados anteriormente en esta sección.
No desencadenes eventos demasiado rápidamente
Algunos eventos del explorador se activan con mucha frecuencia. Por ejemplo, onmousemove y onscroll pueden activarse decenas o cientos de veces por segundo. En la mayoría de los casos, no es necesario realizar actualizaciones de la interfaz de usuario con frecuencia. Si los eventos se desencadenan demasiado rápidamente, puede dañar la capacidad de respuesta de la interfaz de usuario o consumir un tiempo excesivo de CPU.
En vez de usar eventos nativos que se activan rápidamente, considere utilizar JS interoperabilidad para registrar una devolución de llamada que se activa con menos frecuencia. Por ejemplo, el componente siguiente muestra la posición del mouse, pero solo se actualiza como máximo una vez cada 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();
}
El código JavaScript correspondiente registra el agente de escucha de eventos DOM para el movimiento del mouse. En este ejemplo, el agente de escucha de eventos usa la función de throttle Lodash para limitar la tasa de invocaciones:
<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>
Evitar volver a representar tras gestionar eventos que no generen cambios de estado
Los componentes heredan de ComponentBase, el cual invoca automáticamente StateHasChanged después de que los controladores de eventos del componente hayan sido invocados. En algunos casos, podría ser innecesario o no deseado que se vuelva a renderizar después de invocar un manejador de eventos. Por ejemplo, un controlador de eventos podría no modificar el estado del componente. En estos escenarios, la aplicación puede aprovechar la interfaz IHandleEvent para controlar la gestión de eventos de Blazor.
Nota:
El enfoque de esta sección no dirige las excepciones a los límites de error. Para obtener más información y código de demostración que admita límites de error mediante una llamada a ComponentBase.DispatchExceptionAsync, vea AsNonRenderingEventHandler + ErrorBoundary = comportamiento inesperado (dotnet/aspnetcore #54543).
Para evitar que se rendericen nuevamente todos los controladores de eventos de un componente, implemente IHandleEvent y proporcione una IHandleEvent.HandleEventAsync tarea que invoque al controlador de eventos sin llamar a StateHasChanged.
En el ejemplo siguiente, ningún controlador de eventos agregado al componente desencadena una nueva representación, por lo que HandleSelect no genera una nueva representación cuando se invoca.
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);
}
Además de evitar que se realicen rerenderizaciones después de que los manejadores de eventos se activen en un componente de forma global, es posible evitar rerenderizaciones después de un único manejador de eventos mediante el uso del siguiente método auxiliar.
Agregue la siguiente EventUtil clase a una Blazor aplicación. Las acciones y funciones estáticas de la parte superior de la EventUtil clase proporcionan controladores que cubren varias combinaciones de argumentos y tipos de valor devuelto que Blazor se usan al controlar eventos.
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);
}
}
Llame a EventUtil.AsNonRenderingEventHandler para llamar a un controlador de eventos que no active un renderizado cuando se invoque.
En el ejemplo siguiente:
- Al seleccionar el primer botón, que llama a
HandleClick1, se desencadena un nuevo renderizado. - Al seleccionar el segundo botón, que llama a
HandleClick2, no se desencadena una nueva representación. - Al seleccionar el tercer botón, que llama a
HandleClick3, no se desencadena una reestructuración y se hace uso de argumentos de evento (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);
}
}
Además de implementar la IHandleEvent interfaz, aprovechar los otros procedimientos recomendados descritos en este artículo también puede ayudar a reducir los renderizados no deseados tras el manejo de eventos. Por ejemplo, la sobrescritura ShouldRender en componentes secundarios del componente de destino se puede usar para controlar el volver a renderizar.
Evitar volver a crear delegados para muchos elementos o componentes repetidos
BlazorLa recreación de delegados de expresiones lambda para elementos o componentes de un bucle puede dar lugar a un rendimiento deficiente.
El siguiente componente que se muestra en el artículo de control de eventos representa un conjunto de botones. Cada botón asigna un delegado a su @onclick evento, que es correcto si no hay muchos botones que representar.
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}";
}
}
Si se representa un gran número de botones mediante el enfoque anterior, la velocidad de representación se ve afectada negativamente, lo que da lugar a una experiencia de usuario deficiente. Para renderizar un gran número de botones con un callback para eventos de clic, en el ejemplo siguiente se utiliza una colección de objetos de botón que asignan el delegado de cada botón a un @onclick. El enfoque siguiente no requiere Blazor volver a generar todos los delegados de botón cada vez que se representan los botones:
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 => { };
}
}