Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de 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 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Una aplicación Blazor puede invocar funciones de JavaScript (JS) desde métodos .NET y métodos .NET desde funciones de JS. Estos escenarios se denominan interoperabilidad de JavaScript (o interoperabilidad de JS).
En los siguientes artículos encontrará una guía adicional sobre la interoperabilidad de JS:
- Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core
- Llamada a métodos de .NET desde funciones de JavaScript en ASP.NET Core Blazor
Nota:
La API de interoperabilidad de JavaScript[JSImport]
/[JSExport]
está disponible para los componentes del lado del cliente en ASP.NET Core en .NET 7 o posterior.
Para obtener más información, consulta Interoperabilidad JSImport/JSExport de JavaScript con ASP.NET Core Blazor.
Compresión de componentes de servidor interactivos con datos que no son de confianza
Con la compresión, que está habilitada de forma predeterminada, evite crear componentes interactivos del lado servidor (autenticados o autorizados) seguros que representen datos de orígenes que no sean de confianza. Los orígenes que no son de confianza incluyen parámetros de ruta, cadenas de consulta, datos de interoperabilidad JS y cualquier otro origen de datos que un usuario de terceros pueda controlar (bases de datos, servicios externos). Para obtener más información, consulte Guía de ASP.NETBlazorSignalR y Guía de mitigación de amenazas de representación interactiva del lado servidor para ASP.NET Core Blazor.
Paquete de funciones y abstracciones de interoperabilidad de JavaScript
El @microsoft/dotnet-js-interop
paquete (npmjs.com
) (Microsoft.JSInterop
paquete NuGet) proporciona abstracciones y características para la interoperabilidad entre código de .NET y JavaScript (JS). El código fuente de referencia está disponible en el dotnet/aspnetcore
repositorio GitHub (/src/JSInterop
carpeta). Para más información, consulte el archivo README.md
del repositorio GitHub.
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 de una versión específica, usa 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).
Recursos adicionales para escribir scripts de interoperabilidad JS en TypeScript:
- TypeScript
- Tutorial: Crear una aplicación ASP.NET Core con TypeScript en Visual Studio
- Administrar paquetes npm en Visual Studio
Interacción con el DOM
Solo mute el DOM con JavaScript (JS) cuando el objeto no interactúa con Blazor. Blazor mantiene representaciones de DOM e interactúa directamente con objetos DOM. Si un elemento representado por Blazor se modifica externamente mediante JS de forma directa o con la interoperabilidad de JS, DOM puede dejar de coincidir con la representación interna de Blazor, lo que puede provocar un comportamiento indefinido. Puede que el comportamiento indefinido interfiera solamente con la presentación de elementos o sus funciones, pero también puede presentar riesgos de seguridad para la aplicación o el servidor.
Esta guía no solo se aplica a su propio código de interoperabilidad de JS, sino también a todas las bibliotecas de JS que usa la aplicación, incluido todo lo proporcionado por un marco de terceros, como Bootstrap JS y jQuery.
En algunos ejemplos de la documentación, la interoperabilidad de JS se usa para mutar un elemento únicamente con fines de demostración como parte de un ejemplo. En esos casos, aparece una advertencia en el texto.
Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Clase JavaScript con un campo de función de tipo
Una clase de JavaScript con un campo de función de tipo no es compatible con interop BlazorJS. Usa funciones de Javascript en clases.
No compatible:GreetingHelpers.sayHello
en la siguiente clase, como el interop de JS de Blazor no detecta un campo de función de tipo y no se puede ejecutar desde el código C#:
export class GreetingHelpers {
sayHello = function() {
...
}
}
Compatible:GreetingHelpers.sayHello
en la siguiente clase como una función es compatible con:
export class GreetingHelpers {
sayHello() {
...
}
}
También es compatible con funciones de flecha:
export class GreetingHelpers {
sayHello = () => {
...
}
}
Evitar los controladores de eventos en línea
Una función de JavaScript se puede invocar directamente desde un controlador de eventos insertado. En el ejemplo siguiente, alertUser
es una función de JavaScript a la que se llama cuando el usuario selecciona el botón:
<button onclick="alertUser">Click Me!</button>
Sin embargo, el uso de controladores de eventos insertados es una opción de diseño deficiente para llamar a funciones de JavaScript:
- La combinación de marcado HTML y código JavaScript suele dar lugar a código que no se puede mantener.
- Una directiva de seguridad de contenido (CSP) (documentación MDN) puede bloquear la ejecución de un controlador de eventos insertados.
Recomendamos evitar los controladores de eventos insertados en línea en favor de enfoques que asignen controladores en JavaScript con addEventListener
, como se muestra en el siguiente ejemplo:
AlertUser.razor.js
:
export function alertUser() {
alert('The button was selected!');
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", alertUser);
}
AlertUser.razor
:
@page "/alert-user"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Alert User</h1>
<p>
<button id="btn">Click Me!</button>
</p>
@code {
private IJSObjectReference? module;
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/AlertUser.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
Para obtener más información, consulte los siguientes recursos:
- Ubicación de JavaScript en aplicaciones Blazor de ASP.NET Core
- Introducción a los eventos (documentación de MDN)
Llamadas asincrónicas de JavaScript
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 garantizar que los componentes sean compatibles entre los modelos de representación del lado del servidor y del lado del cliente. Al adoptar el renderizado del lado del servidor, JS las llamadas interoperables deben ser asíncronas porque se envían a través de una conexión de red. Para las aplicaciones que adoptan exclusivamente la renderización del lado del cliente, se admiten las llamadas interoperativas síncronas JS.
Para obtener más información, vea los artículos siguientes:
Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Serialización de objetos
Blazor usa System.Text.Json para la serialización con los siguientes requisitos y comportamientos predeterminados:
- Los tipos deben tener un constructor predeterminado, los
get
/set
descriptores de acceso deben ser públicos y los campos nunca se serializan. - La serialización predeterminada global no es personalizable para evitar que se separen las bibliotecas de componentes existentes y que haya consecuencias en el rendimiento y la seguridad, además de una reducción de la fiabilidad.
- La serialización de nombres de miembros de .NET da como resultado nombres de clave de JSON en minúsculas.
- JSON se deserializa como instancias de C# JsonElement, lo que permite un uso mixto de mayúsculas y minúsculas. La conversión interna para la asignación de propiedades de modelos de C# funciona según lo previsto a pesar de las diferencias en el uso de mayúsculas y minúsculas en nombres de clave de JSON y nombres de propiedades de C#.
- Los tipos de marcos complejos, como KeyValuePair, pueden recortarse mediante el optimizador de IL en la publicación y no están presentes para la interoperabilidad JS. Se recomienda crear tipos personalizados para los tipos que el recortador de IL recorta.
- Blazor siempre se basa en la reflexión para la serialización JSON, incluso cuando se usa la generación de origen de C#. Al establecer
JsonSerializerIsReflectionEnabledByDefault
enfalse
en el archivo de proyecto de la aplicación, se produce un error al intentar la serialización.
La API de JsonConverter está disponible para la serialización personalizada. Las propiedades se pueden anotar con un atributo de [JsonConverter]
para invalidar la serialización predeterminada de un tipo de datos existente.
Para más información, consulte los recursos siguientes en la documentación de .NET:
- Serizalización y deserialización de JSON (marshalling y unmarshalling) en .NET
- Cómo personalizar los nombres y valores de propiedades con
System.Text.Json
- Procedimiento para escribir convertidores personalizados para la serialización de JSON (cálculo de referencias) en .NET
Blazor admite la interoperabilidad de JS de matriz de bytes optimizada que evita la codificación o descodificación de matrices de bytes en Base64. La aplicación puede aplicar la serialización personalizada y pasar los bytes resultantes. Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Blazor admite interoperabilidad JS sin formato cuando un gran volumen de objetos .NET se serializa rápidamente o cuando se deben serializar objetos .NET grandes o muchos objetos .NET. Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Tareas de limpieza del DOM durante la eliminación de componentes
No ejecute código de interoperabilidad de JS para las tareas de limpieza de DOM durante la eliminación de componentes. En su lugar, utilice el patrón MutationObserver
en JavaScript (JS) en el cliente por las siguientes razones:
- Es posible que el componente se haya quitado del DOM en el momento en que se ejecute el código de limpieza en
Dispose{Async}
. - Durante la representación del lado del servidor, el framework puede haber eliminado la representación Blazor en el momento en que se ejecuta el código de limpieza en
Dispose{Async}
.
El patrón MutationObserver
permite ejecutar una función cuando se quita un elemento del DOM.
En el siguiente ejemplo, el componente DOMCleanup
:
- Contiene un
<div>
con unid
decleanupDiv
. El elemento<div>
se elimina del DOM junto con el rest del marcado DOM del componente cuando este se elimina del DOM. - Carga la clase
DOMCleanup
JS desde el archivoDOMCleanup.razor.js
y llama a su funcióncreateObserver
para establecer la llamada de retornoMutationObserver
. Estas tareas se llevan a cabo en elOnAfterRenderAsync
método del ciclo de vida.
DOMCleanup.razor
:
@page "/dom-cleanup"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>DOM Cleanup Example</h1>
<div id="cleanupDiv"></div>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>(
"import", "./Components/Pages/DOMCleanup.razor.js");
await module.InvokeVoidAsync("DOMCleanup.createObserver");
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
En el siguiente ejemplo, la llamada MutationObserver
de retorno se ejecuta cada vez que se produce un cambio en el DOM. Ejecute su código de limpieza cuando la declaraciónif
confirme que el elemento de destino (cleanupDiv
) ha sido eliminado (if (targetRemoved) { ... }
). Es importante desconectar y borrar el MutationObserver
para evitar una fuga de memoria después de que se ejecute el código de limpieza.
DOMCleanup.razor.js
colocado junto al componente anterior DOMCleanup
:
export class DOMCleanup {
static observer;
static createObserver() {
const target = document.querySelector('#cleanupDiv');
this.observer = new MutationObserver(function (mutations) {
const targetRemoved = mutations.some(function (mutation) {
const nodes = Array.from(mutation.removedNodes);
return nodes.indexOf(target) !== -1;
});
if (targetRemoved) {
// Cleanup resources here
// ...
// Disconnect and delete MutationObserver
this.observer && this.observer.disconnect();
delete this.observer;
}
});
this.observer.observe(target.parentNode, { childList: true });
}
}
window.DOMCleanup = DOMCleanup;
Llamadas de interoperabilidad de JavaScript sin un circuito
Esta sección solo se aplica a las aplicaciones del lado del servidor.
Las llamadas de interoperabilidad de JavaScript (JS) no se pueden emitir después de desconectar un circuito SignalR. Sin un circuito durante la eliminación de componentes o en cualquier otro momento en que no exista un circuito, se produce un error en las siguientes llamadas de método y se registra un mensaje que indica que el circuito está desconectado como JSDisconnectedException:
- Llamadas de método de interoperabilidad JS
Dispose
/DisposeAsync
llama a cualquier IJSObjectReference.
Para evitar el registro de JSDisconnectedException o para registrar información personalizada, capture la excepción en una instrucción try-catch
.
Para el ejemplo de eliminación de componentes siguiente:
- El componente implementa IAsyncDisposable.
objInstance
es una IJSObjectReference.- JSDisconnectedException se captura y no se registra.
- Opcionalmente, puede registrar información personalizada en la instrucción
catch
en el nivel de registro que prefiera. En el ejemplo siguiente no se registra información personalizada porque se supone que al desarrollador no le importa cuándo o dónde se desconectan los circuitos durante la eliminación de componentes.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (objInstance is not null)
{
await objInstance.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Si debe limpiar sus propios objetos JS o ejecutar código de JS adicional en el cliente después de perder un circuito, use el patrón MutationObserver
en JS en el cliente. El patrón MutationObserver
permite ejecutar una función cuando se quita un elemento del DOM.
Para más información, consulte los siguientes artículos.
- Control de errores en aplicaciones de Blazor de ASP.NET Core: en la sección Interoperabilidad de JavaScript se describe el control de errores en escenarios de interoperabilidad de JS.
- Ciclo de vida de los componentes Razor de ASP.NET Core: en la sección Eliminación de componentes con
IDisposable
yIAsyncDisposable
se describe cómo implementar patrones de eliminación en componentes Razor.
Archivos JavaScript almacenados en caché
Los archivos de JavaScript (JS) y otros recursos estáticos no suelen almacenarse en caché en los clientes durante el desarrollo en el Development
entorno. Durante el desarrollo, las solicitudes de recursos estáticos incluyen el encabezado Cache-Control
con un valor de no-cache
o max-age
con un valor de cero (0
).
Durante la producción en el entorno Production
, los clientes suelen almacenar en caché los archivos JS.
Para deshabilitar el almacenamiento en caché del lado cliente en los exploradores, los desarrolladores suelen adoptar uno de los enfoques siguientes:
- Deshabilite el almacenamiento en caché cuando la consola de herramientas de desarrollo del explorador esté abierta. Puede encontrar instrucciones en la documentación de las herramientas de desarrollo de cada manteador del explorador:
- Realice una actualización manual del explorador en cualquier página web de la aplicación Blazor para volver a cargar los archivos JS desde el servidor. Middleware de almacenamiento en caché HTTP de ASP.NET Core siempre respeta un encabezado
Cache-Control
no-cache válido enviado por un cliente.
Para más información, consulte:
Límites de tamaño en las llamadas de interoperabilidad de JavaScript
Esta sección solo se aplica a los componentes interactivos de las aplicaciones del lado del servidor. Para los componentes del lado del cliente, el framework no impone un límite al tamaño de las entradas y salidas de interoperabilidad de JavaScript (JS).
En el caso de los componentes interactivos de las aplicaciones del lado del servidor, el tamaño de las llamadas interoperativas JS que pasan datos del cliente al servidor está limitado por el tamaño máximo de los mensajes entrantes SignalR permitido para los métodos del centro, que se aplica mediante HubOptions.MaximumReceiveMessageSize (por defecto: 32 KB). Los mensajes de JS a .NET SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente. Para obtener más información sobre el límite de tamaño, los mensajes de error y una guía para administrar los límites de tamaño de los mensajes, consulte la guía BlazorSignalR ASP.NET Core.
Determinar dónde se ejecuta la aplicación
Si es relevante que la aplicación sepa dónde se ejecuta código para las llamadas de interoperabilidad de JS, use OperatingSystem.IsBrowser para determinar si el componente se está ejecutando en el contexto del explorador en WebAssembly.