Interoperabilidade de JavaScript e ASP.NET Core Blazor (interoperabilidade de JS)
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Um aplicativo Blazor pode invocar funções JavaScript (JS) de métodos .NET e métodos .NET de funções JS. Esses cenários são chamados de interoperabilidade entre e JavaScript (interoperabilidade de JS).
Outras diretrizes de interoperabilidade de JS são fornecidas nos seguintes artigos:
- Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor
- Chamar métodos .NET de funções JavaScript no ASP.NET Core Blazor
Observação
A API de interoperabilidade do JavaScript [JSImport]
/[JSExport]
está disponível para componentes do lado do cliente no ASP.NET Core no .NET 7 ou posterior.
Para obter mais informações, consulte Interoperabilidade de JSImport/JSExport de JavaScript com o ASP.NET Core Blazor.
Compactação para componentes de servidor interativos com dados não confiáveis
Com a compressão, que está habilitada por padrão, evite criar componentes interativos seguros (autenticados/autorizados) do lado do servidor que renderizam dados de fontes não confiáveis. As fontes não confiáveis incluem parâmetros de rota, cadeias de caracteres de consulta, dados de interoperação com JS e qualquer outra fonte de dados que um usuário de terceiros possa controlar (bancos de dados, serviços externos). Para obter mais informações, consulte ASP.NET Core BlazorSignalRDiretrizes e Orientações de mitigação de ameaças para o ASP.NET Core Blazor renderização interativa do lado do servidor.
Pacote de recursos e abstrações de interoperabilidade do JavaScript
O pacote @microsoft/dotnet-js-interop
(npmjs.com
) (pacote Microsoft.JSInterop
NuGet) fornece abstrações e recursos para interoperabilidade entre o código .NET e JavaScript (JS). A fonte de referência está disponível no dotnet/aspnetcore
repositório do GitHub (pasta /src/JSInterop
). Para obter mais informações, consulte o arquivo README.md
do repositório do GitHub.
Observação
Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Recursos adicionais para gravar scripts de interoperabilidade JS no TypeScript:
- TypeScript
- Tutorial: Criar um aplicativo ASP.NET Core com TypeScript no Visual Studio
- Gerenciar pacotes de npm no Visual Studio
Interação com o DOM
Altere o DOM com JavaScript (JS) somente quando o objeto não interagir com Blazor. Blazor mantém representações do DOM e interage diretamente com objetos DOM. Se um elemento renderizado por Blazor for modificado externamente usando JS diretamente ou via interoperabilidade com JS, o DOM poderá não corresponder mais à representação interna de Blazor, o que poderá resultar em um comportamento indefinido. O comportamento indefinido pode apenas interferir na apresentação de elementos ou de suas funções, mas também pode introduzir riscos de segurança ao aplicativo ou servidor.
Essa diretriz não se aplica apenas ao seu código de interoperabilidade de JS, mas também a todas as bibliotecas JS que o aplicativo usa, incluindo qualquer coisa fornecida por uma estrutura de terceiros, como Bootstrap JS e jQuery.
Em alguns exemplos de documentação, a interoperabilidade de JS é usada para alterar um elemento puramente para fins de demonstração como parte de um exemplo. Nesses casos, um aviso aparece no texto.
Para obter mais informações, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.
Classe JavaScript com um campo do tipo função
Uma classe JavaScript com um campo do tipo função não é compatível com BlazorJS interop. Use funções Javascript nas classes.
Sem suporte:GreetingHelpers.sayHello
na classe a seguir, uma vez que um campo do tipo função não é descoberto pela interop de Blazor JS e não pode ser executado a partir do código C#:
export class GreetingHelpers {
sayHello = function() {
...
}
}
Com suporte:GreetingHelpers.sayHello
na seguinte classe, uma vez que uma função é suportada:
export class GreetingHelpers {
sayHello() {
...
}
}
As funções de seta também são suportadas:
export class GreetingHelpers {
sayHello = () => {
...
}
}
Evitar manipuladores de eventos em linha
Uma função JavaScript pode ser invocada diretamente de um manipulador de eventos em linha. No exemplo a seguir, alertUser
é uma função JavaScript chamada quando o botão é selecionado pelo usuário:
<button onclick="alertUser">Click Me!</button>
No entanto, o uso de manipuladores de eventos em linha é uma opção de design ruim para chamar funções JavaScript:
- A mistura de marcação HTML e código JavaScript geralmente leva a um código insustentável.
- A execução do manipulador de eventos em linha pode ser bloqueada por uma CSP (Política de Segurança de Conteúdo) (documentação do MDN).
Recomendamos evitar manipuladores de eventos em linha em favor de abordagens que atribuem manipuladores em JavaScript com addEventListener
, como demonstra o exemplo a seguir:
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 saber mais, consulte os recursos a seguir:
- Localização do JavaScript em aplicativos ASP.NET Core Blazor
- Introdução aos eventos (documentação do MDN)
Chamadas de JavaScript assíncronas
As chamadas de interoperabilidade JS são assíncronas, independentemente de o código chamado ser síncrono ou assíncrono. As chamadas são assíncronas para garantir que os componentes sejam compatíveis nos modelos de renderização do lado do servidor e do cliente. Ao adotar a renderização do lado do servidor, as chamadas de interoperabilidade de JS devem ser assíncronas porque são enviadas por uma conexão de rede. Para aplicativos que adotam exclusivamente a renderização do lado do cliente, há suporte para as chamadas de interoperabilidade de JS.
Para obter mais informações, consulte os seguintes artigos:
Para obter mais informações, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.
Serialização de objeto
Blazor usa System.Text.Json para serialização com os seguintes requisitos e comportamentos padrão:
- Os tipos devem ter um construtor padrão,
get
/set
os acessadores de devem ser públicos e os campos nunca são serializados. - A serialização padrão global não é personalizável para evitar a quebra de bibliotecas de componentes existentes, impactos sobre o desempenho e a segurança e reduções na confiabilidade.
- A serialização de nomes de membros do .NET resulta em nomes de chave JSON minúsculos.
- O JSON é desserializado como instâncias de C# JsonElement, que permitem a combinação de maiúsculas e minúsculas. A conversão interna para atribuição a propriedades do modelo C# funciona conforme o esperado, apesar de eventuais diferenças de maiúsculas e minúsculas entre nomes de chave JSON e nomes de propriedades C#.
- Tipos de estrutura complexos, como KeyValuePair, podem ser cortados pelo IL Trimmer no momento da publicação e não estarem presentes para a interoperabilidade com o JS. Recomendamos a criação de tipos personalizados para tipos que o IL Trimmer corta.
- Blazor sempre depende de reflexão para serialização JSON, inclusive ao usar geração de origem C#. Definir
JsonSerializerIsReflectionEnabledByDefault
comofalse
no arquivo de projeto do aplicativo resulta em um erro ao tentar a serialização.
A API JsonConverter está disponível para serialização personalizada. As propriedades podem ser anotadas com um atributo [JsonConverter]
para substituir a serialização padrão para um tipo de dados existente.
Para obter mais informações, consulte os seguintes recursos na documentação do .NET:
- Serialização e desserialização de JSON (marshalling e unmarshalling) no .NET
- Como personalizar nomes e valores de propriedade com
System.Text.Json
- Como gravar conversores personalizados para serialização JSON (marshalling) no .NET
Blazor dá suporte à interoperabilidade de JS da matriz de bytes otimizada que evita codificar/decodificar matrizes de bytes em Base64. O aplicativo pode aplicar a serialização personalizada e passar os bytes resultantes. Para obter mais informações, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.
Blazor dá suporte à interoperabilidade de JS sem marshaling quando um grande volume de objetos .NET é serializado rapidamente ou quando objetos .NET grandes ou muitos objetos .NET devem ser serializados. Para obter mais informações, consulte Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor.
Tarefas de limpeza do DOM no descarte de componentes
Não execute código de interoperabilidade JS para tarefas de limpeza do DOM durante o descarte de componentes. Em vez disso, use o padrão MutationObserver
no JavaScript (JS) no cliente pelos seguintes motivos:
- O componente pode ter sido removido do DOM no momento em que o código de limpeza foi executado no
Dispose{Async}
. - Durante a renderização do lado do cliente, o renderizador Blazor pode ter sido descartado pela estrutura no momento em que o código de limpeza foi executado em
Dispose{Async}
.
O padrão MutationObserver
permite executar uma função quando um elemento é removido do DOM.
No exemplo a seguir, o componente DOMCleanup
:
- Contém um
<div>
com umid
decleanupDiv
. O elemento<div>
é removido do DOM junto com rest da marcação do DOM do componente quando o componente é removido do DOM. - Carrega a classe
DOMCleanup
JS do arquivoDOMCleanup.razor.js
e chama sua funçãocreateObserver
para configurar o retorno de chamadaMutationObserver
. Essas tarefas são realizadas noOnAfterRenderAsync
método de 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();
}
}
}
No exemplo a seguir, o retorno de chamada MutationObserver
é executado sempre que ocorre uma alteração do DOM. Execute o código de limpeza quando a instrução if
confirmar que o elemento de destino (cleanupDiv
) foi removido (if (targetRemoved) { ... }
). É importante desconectar e excluir o MutationObserver
para evitar um vazamento de memória após a execução do código de limpeza.
DOMCleanup.razor.js
colocado lado a lado com o componente DOMCleanup
anterior:
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;
Chamadas de interoperabilidade do JavaScript sem um circuito
Esta seção só se aplica a aplicativos do lado do servidor.
As chamadas de interoperabilidade do JavaScript (JS) não podem ser emitidas depois que um circuito SignalR é desconectado. Sem um circuito durante o descarte de componentes ou em qualquer outro momento em que um circuito não exista, as chamadas de método a seguir falham e registram uma mensagem, informando que o circuito está desconectado como um JSDisconnectedException:
- chamadas de método de interoperabilidade JS
Dispose
/DisposeAsync
chama em qualquer IJSObjectReference.
Para evitar o log de JSDisconnectedException ou de informações personalizadas, capture a exceção em uma instrução try-catch
.
No seguinte exemplo de descarte de componentes:
- O componente implementa IAsyncDisposable.
objInstance
é um IJSObjectReference.- JSDisconnectedException é capturado e não registrado.
- Opcionalmente, você pode registrar informações personalizadas na instrução
catch
em qualquer nível de log que preferir. O exemplo a seguir não registra informações personalizadas porque pressupõe que o desenvolvedor não se importa com quando ou onde os circuitos são desconectados durante o descarte de componentes.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (objInstance is not null)
{
await objInstance.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Se você precisar limpar seus próprios objetos JS ou executar outro código JS no cliente, depois que um circuito for perdido, use o padrão MutationObserver
em JS no cliente. O padrão MutationObserver
permite executar uma função quando um elemento é removido do DOM.
Para obter mais informações, consulte os seguintes artigos:
- Tratar erros em aplicativos Blazor do ASP.NET Core: a seção Interoperabilidade do JavaScript discute o tratamento de erros em cenários de interoperabilidade JS.
- Ciclo de vida do componente Razor do ASP.NET Core: a seção Descarte de componentes com
IDisposable
eIAsyncDisposable
descreve como implementar padrões de descarte em componentes Razor.
Arquivos JavaScript armazenados em cache
Arquivos JavaScript (JS) e outros ativos estáticos geralmente não são armazenados em cache em clientes durante o desenvolvimento no ambiente Development
. Durante o desenvolvimento, as solicitações de ativo estático incluem o cabeçalho Cache-Control
com um valor de no-cache
ou max-age
com um valor zero (0
).
Durante a produção no ambiente Production
, os arquivos JS geralmente são armazenados em cache pelos clientes.
Para desabilitar o cache do lado do cliente em navegadores, os desenvolvedores geralmente adotam uma das seguintes abordagens:
- Desabilitar o cache quando o console de ferramentas de desenvolvedor do navegador estiver aberto. Diretrizes podem ser encontradas na documentação de ferramentas de desenvolvedor de cada mantenedor do navegador:
- Execute uma atualização manual do navegador de qualquer página da Web do aplicativo Blazor para recarregar arquivos JS do servidor. O Middleware de Cache HTTP do ASP.NET Core sempre honra um cabeçalho
Cache-Control
no-cache enviado por um cliente.
Para saber mais, veja:
Limites de tamanho em chamadas de interoperabilidade do JavaScript
Esta seção só se aplica a componentes interativos em aplicativos do lado do servidor. Para componentes do lado do cliente, a estrutura não impõe um limite ao tamanho das entradas e saídas de interoperabilidade do JavaScript (JS).
Para componentes interativos em aplicativos do lado do servidor, chamadas de interoperabilidade de JS passando dados do cliente para o servidor são limitadas em tamanho pelo tamanho máximo de mensagem de entrada SignalR permitido pelos métodos de hub, que é imposto por HubOptions.MaximumReceiveMessageSize (padrão: 32 KB). As mensagens do JS para o SignalR do .NET maiores que MaximumReceiveMessageSize geram um erro. A estrutura não impõe um limite no tamanho de uma mensagem SignalR do hub para um cliente. Para obter mais informações sobre o limite de tamanho, mensagens de erro e as diretrizes sobre como lidar com os limites de tamanho da mensagem, consulte Diretrizes do BlazorSignalRASP.NET Core.
Determine em que local o aplicativo está sendo executado
Se for relevante para o aplicativo saber em que ponto o código está sendo executado para as chamadas de interoperabilidade JS, use OperatingSystem.IsBrowser para determinar se o componente está sendo executado no contexto do navegador no WebAssembly.