Compartir a través de


Interoperabilidad de JavaScript (Blazor interop) procedimientos recomendados de rendimiento de ASP.NET Core JS

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.

Las llamadas entre .NET y JavaScript requieren sobrecarga adicional porque:

  • Las llamadas son asincrónicas.
  • Los parámetros y los valores devueltos se serializan mediante JSON para proporcionar un mecanismo de conversión fácil de entender entre los tipos de .NET y JavaScript.

Además, para las aplicaciones del lado del servidor Blazor, estas llamadas circulan a través de la red.

Evitar llamadas excesivamente específicas

Dado que cada llamada implica cierta sobrecarga, puede ser útil reducir el número de llamadas. Tenga en cuenta el código siguiente, que almacena una colección de elementos en el explorador localStorage:

private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
    foreach (var item in items)
    {
        await JS.InvokeVoidAsync("localStorage.setItem", item.Id, 
            JsonSerializer.Serialize(item));
    }
}

En el ejemplo anterior se realiza una llamada de interoperabilidad independiente JS para cada elemento. En su lugar, el siguiente enfoque simplifica la interoperabilidad JS a una única llamada.

private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
    await JS.InvokeVoidAsync("storeAllInLocalStorage", items);
}

La función de JavaScript correspondiente almacena toda la colección de elementos en el cliente:

function storeAllInLocalStorage(items) {
  items.forEach(item => {
    localStorage.setItem(item.id, JSON.stringify(item));
  });
}

Para Blazor WebAssembly aplicaciones, agrupar llamadas de interoperabilidad individuales JS en una sola llamada suele mejorar significativamente el rendimiento solo si el componente realiza un gran número de llamadas de JS interoperabilidad.

Considere el uso de llamadas sincrónicas

Llamada a JavaScript desde .NET

Esta sección solo se aplica a los componentes del lado cliente.

Las llamadas de interop de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asincrónicas para asegurarse de que los componentes son compatibles entre los modos de representación del lado servidor y del lado cliente. En el servidor, todas las JS llamadas de interoperabilidad deben ser asincrónicas porque se envían a través de una conexión de red.

Si sabe que el componente solo se ejecuta en WebAssembly, puede optar por realizar llamadas de interoperabilidad sincrónicas JS . Esto tiene una sobrecarga ligeramente menor que realizar llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras espera los resultados.

Para realizar una llamada sincrónica de .NET a JavaScript en un componente del lado cliente, convierta IJSRuntime a IJSInProcessRuntime para realizar la llamada de interoperabilidad JS.

@inject IJSRuntime JS

...

@code {
    protected override void HandleSomeEvent()
    {
        var jsInProcess = (IJSInProcessRuntime)JS;
        var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
    }
}

Al trabajar con IJSObjectReference en componentes del lado cliente de .NET 5 o posteriores, puede usar IJSInProcessObjectReference de forma sincrónica en su lugar. IJSInProcessObjectReference implementa IAsyncDisposable/IDisposable y debe desecharse para la recolección de basura para evitar una pérdida de memoria, como se muestra en el siguiente ejemplo:

@inject IJSRuntime JS
@implements IDisposable

...

@code {
    ...
    private IJSInProcessObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import", 
                "./scripts.js");
            var value = module.Invoke<string>("javascriptFunctionIdentifier");
        }
    }

    ...

    void IDisposable.Dispose()
    {
        if (module is not null)
        {
            await module.Dispose();
        }
    }
}

En el ejemplo anterior, durante la eliminación del módulo, no se atrapa a un JSDisconnectedException porque no hay ningún circuito BlazorSignalR en una aplicación Blazor WebAssembly que perder. Para obtener más información, consulta ASP.NET Core Blazor Interoperabilidad de JavaScript (JS interop).

Llamada a .NET desde JavaScript

Esta sección solo se aplica a los componentes del lado cliente.

Las llamadas de interop de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asincrónicas para asegurarse de que los componentes son compatibles entre los modos de representación del lado servidor y del lado cliente. En el servidor, todas las JS llamadas de interoperabilidad deben ser asincrónicas porque se envían a través de una conexión de red.

Si sabe que el componente solo se ejecuta en WebAssembly, puede optar por realizar llamadas de interoperabilidad sincrónicas JS . Esto tiene una sobrecarga ligeramente menor que realizar llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras espera los resultados.

Para realizar una llamada sincrónica de JavaScript a .NET en un componente del lado cliente, use DotNet.invokeMethod en lugar de DotNet.invokeMethodAsync.

Las llamadas sincrónicas funcionan si:

  • El componente solo se representa para su ejecución en WebAssembly.
  • La función llamada devuelve un valor sincrónicamente. La función no es un async método y no devuelve .NET Task ni JavaScript Promise.

Esta sección solo se aplica a los componentes del lado cliente.

Las llamadas de interop de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asincrónicas para asegurarse de que los componentes son compatibles entre los modos de representación del lado servidor y del lado cliente. En el servidor, todas las JS llamadas de interoperabilidad deben ser asincrónicas porque se envían a través de una conexión de red.

Si sabe que el componente solo se ejecuta en WebAssembly, puede optar por realizar llamadas de interoperabilidad sincrónicas JS . Esto tiene una sobrecarga ligeramente menor que realizar llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras espera los resultados.

Para realizar una llamada sincrónica de .NET a JavaScript en un componente del lado cliente, convierta IJSRuntime a IJSInProcessRuntime para realizar la llamada de interoperabilidad JS.

@inject IJSRuntime JS

...

@code {
    protected override void HandleSomeEvent()
    {
        var jsInProcess = (IJSInProcessRuntime)JS;
        var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
    }
}

Al trabajar con IJSObjectReference en componentes del lado cliente de .NET 5 o posteriores, puede usar IJSInProcessObjectReference de forma sincrónica en su lugar. IJSInProcessObjectReference implementa IAsyncDisposable/IDisposable y debe desecharse para la recolección de basura para evitar una pérdida de memoria, como se muestra en el siguiente ejemplo:

@inject IJSRuntime JS
@implements IDisposable

...

@code {
    ...
    private IJSInProcessObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import", 
                "./scripts.js");
            var value = module.Invoke<string>("javascriptFunctionIdentifier");
        }
    }

    ...

    void IDisposable.Dispose()
    {
        if (module is not null)
        {
            await module.Dispose();
        }
    }
}

En el ejemplo anterior, durante la eliminación del módulo, no se atrapa a un JSDisconnectedException porque no hay ningún circuito BlazorSignalR en una aplicación Blazor WebAssembly que perder. Para obtener más información, consulta ASP.NET Core Blazor Interoperabilidad de JavaScript (JS interop).

Considere el uso de llamadas no serializadas

Esta sección solo se aplica a las Blazor WebAssembly aplicaciones.

Cuando se ejecuta en Blazor WebAssembly, es posible realizar llamadas no definidas desde .NET a JavaScript. Se trata de llamadas sincrónicas que no realizan la serialización JSON de argumentos o valores devueltos. Todos los aspectos de la administración de memoria y las traducciones entre representaciones de .NET y JavaScript se dejan al desarrollador.

Advertencia

Aunque IJSUnmarshalledRuntime tiene la menor sobrecarga de los enfoques de interoperabilidad JS, las API de JavaScript necesarias para interactuar con estas APIs no están documentadas actualmente y están sujetas a cambios significativos en futuras versiones.

function jsInteropCall() {
  return BINDING.js_to_mono_obj("Hello world");
}
@inject IJSRuntime JS

@code {
    protected override void OnInitialized()
    {
        var unmarshalledJs = (IJSUnmarshalledRuntime)JS;
        var value = unmarshalledJs.InvokeUnmarshalled<string>("jsInteropCall");
    }
}

Utilizar la interoperabilidad de JavaScript [JSImport]/[JSExport]

La interoperabilidad de JavaScript [JSImport]/[JSExport] para aplicaciones Blazor WebAssembly ofrece un mejor rendimiento y estabilidad en comparación con la API de interoperabilidad JS en los lanzamientos de framework anteriores a ASP.NET Core en .NET 7.

Para obtener más información, consulta Interoperabilidad JSImport/JSExport de JavaScript con ASP.NET Core Blazor.