Interoperabilidade [JSImport]/[JSExport] do JavaScript com ASP.NET Core Blazor

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.

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.

Este artigo explica como interagir com o JavaScript (JS) em componentes do lado do cliente usando a API de interoperabilidade do JavaScript (JS) [JSImport]/[JSExport] lançada nos aplicativos que adotam o .NET 7 ou posterior.

O Blazor fornece seu próprio mecanismo de interoperabilidade JS com base na interface IJSRuntime. A interoperabilidade do Blazor para JS é uniformemente suportada nos diversos modos de renderização do Blazor e para os aplicativos Blazor Hybrid. O IJSRuntime permite que os autores de bibliotecas compilem bibliotecas de interoperabilidade JS que podem ser compartilhadas em todo o ecossistema Blazor e continua sendo a abordagem recomendada para a interoperabilidade JS no Blazor. Veja os artigos a seguir:

Este artigo descreve uma abordagem de interoperabilidade JS específica aos componentes do lado do cliente executados no WebAssembly. Essas abordagens são apropriadas quando você espera apenas executar no WebAssembly do lado do cliente. Os autores de bibliotecas podem usar essas abordagens para otimizar a interoperabilidade JS ao verificar, durante a execução do código, se o aplicativo está em execução no WebAssembly em um navegador (OperatingSystem.IsBrowser). As abordagens descritas neste artigo devem ser usadas para substituir a API de interoperabilidade JS sem realizar unmarshaling ao migrar para o .NET 7 ou posterior.

Observação

Este artigo se concentra na interoperabilidade JS em componentes do lado do cliente. Para obter diretrizes sobre como chamar o .NET em aplicativos JavaScript, consulte Executar o .NET do JavaScript.

API de interoperabilidade JavaScript obsoleta

A realização de unmarshaling de interoperabilidade JS usando a API IJSUnmarshalledRuntime está obsoleto no ASP.NET Core no .NET 7 ou posterior. Siga as diretrizes neste artigo para substituir a API obsoleta.

Pré-requisitos

Baixar e instala o .NET 7 ou posterior se ele ainda não estiver instalado no sistema ou se o sistema não tiver a versão mais recente instalada.

Namespace

A API de interoperabilidade JS descrita neste artigo é controlada por atributos no namespace System.Runtime.InteropServices.JavaScript.

Habilitar blocos não seguros

Habilite a propriedade AllowUnsafeBlocks no arquivo de projeto do aplicativo, que permite que o gerador de código no compilador Roslyn use ponteiros para JS interoperabilidade:

<PropertyGroup>
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

Aviso

A API de interoperabilidade JS requer a habilitação de AllowUnsafeBlocks. Tenha cuidado ao implementar seu próprio código não seguro em aplicativos .NET, o que pode introduzir riscos de segurança e estabilidade. Para obter mais informações, consulte Código não seguro, tipos de ponteiro e ponteiros de função.

Chamar JavaScript do .NET

Esta seção explica como chamar funções JS do .NET.

No seguinte componente CallJavaScript1:

  • O módulo CallJavaScript1 é importado de forma assíncrona do arquivo JS posicionado com JSHost.ImportAsync.
  • A função getMessageJS importada é chamada por GetWelcomeMessage.
  • A cadeia de caracteres de mensagem de boas-vindas retornada é exibida na interface do usuário por meio do campo message.

CallJavaScript1.razor:

@page "/call-javascript-1"
@rendermode InteractiveWebAssembly
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 1)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override async Task OnInitializedAsync()
    {
        await JSHost.ImportAsync("CallJavaScript1", 
            "../Components/Pages/CallJavaScript1.razor.js");

        message = GetWelcomeMessage();
    }
}
@page "/call-javascript-1"
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 1)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override async Task OnInitializedAsync()
    {
        await JSHost.ImportAsync("CallJavaScript1", 
            "../Pages/CallJavaScript1.razor.js");

        message = GetWelcomeMessage();
    }
}

Observação

Inclui um código de verificação condicional com OperatingSystem.IsBrowser para garantir que a interoperabilidade JS seja chamada apenas por um componente renderizado no cliente. Isso é importante para bibliotecas/pacotes NuGet direcionados a componentes do lado do servidor, que não podem executar o código fornecido por essa API de interoperabilidade JS.

Para importar uma função JS para chamá-la de C#, use o atributo [JSImport] em uma assinatura de método C# que corresponda à assinatura de função JS. O primeiro parâmetro para o atributo [JSImport] é o nome da função JS a ser importada e o segundo parâmetro é o nome do módulo JS.

No exemplo a seguir, getMessage é uma função JS que retorna um string para um módulo nomeado CallJavaScript1. A assinatura do método C# corresponde: nenhum parâmetro é passado para a função JS e a função JS retorna um string. A função JS é denominada por GetWelcomeMessage no código C#.

CallJavaScript1.razor.cs:

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Components.Pages;

[SupportedOSPlatform("browser")]
public partial class CallJavaScript1
{
    [JSImport("getMessage", "CallJavaScript1")]
    internal static partial string GetWelcomeMessage();
}

O namespace do aplicativo para a classe parcial CallJavaScript1 anterior é BlazorSample. O namespace do componente será BlazorSample.Components.Pages. Se estiver usando o componente anterior em um aplicativo de teste local, atualize o namespace para corresponder ao aplicativo. Por exemplo, o namespace será ContosoApp.Components.Pages se o namespace do aplicativo for ContosoApp. Para saber mais, consulte Componentes Razor do ASP.NET Core.

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Pages;

[SupportedOSPlatform("browser")]
public partial class CallJavaScript1
{
    [JSImport("getMessage", "CallJavaScript1")]
    internal static partial string GetWelcomeMessage();
}

O namespace do aplicativo para a classe parcial CallJavaScript1 anterior é BlazorSample. O namespace do componente será BlazorSample.Pages. Se estiver usando o componente anterior em um aplicativo de teste local, atualize o namespace para corresponder ao aplicativo. Por exemplo, o namespace será ContosoApp.Pages se o namespace do aplicativo for ContosoApp. Para saber mais, consulte Componentes Razor do ASP.NET Core.

Na assinatura do método importado, você pode usar tipos .NET para parâmetros e valores retornados, que sofrem realização de marshal automaticamente pelo runtime. Use JSMarshalAsAttribute<T> para controlar como os parâmetros do método importado sofrem realização de marshal. Por exemplo, você pode optar por realizar marshaling de um long como System.Runtime.InteropServices.JavaScript.JSType.Number ou System.Runtime.InteropServices.JavaScript.JSType.BigInt. Você pode passar Action/Func<TResult> retornos de chamada como parâmetros, que sofrem realização de marshal como funções chamáveis JS. Você pode passar referências de JS e objeto gerenciado e elas sofrem realização de marshal como objetos proxy, mantendo o objeto ativo no limite até que o proxy seja coletado. Você também pode importar e exportar métodos assíncronos com um resultado Task, que sofrem realização de marshal como JS promessas. A maioria dos tipos com marshalling funciona em ambas as direções, como parâmetros e como valores retornados, em métodos importados e exportados, que são abordados na seção Chamar .NET do JavaScript mais adiante neste artigo.

A tabela a seguir indica os mapeamentos de tipo com suporte.

.NET JavaScript Nullable TaskparaPromise Opcional JSMarshalAs Array of
Boolean Boolean Com suporte Com suporte Com suporte Sem suporte
Byte Number Com suporte Com suporte Com suporte Com suporte
Char String Com suporte Com suporte Com suporte Sem suporte
Int16 Number Com suporte Com suporte Com suporte Sem suporte
Int32 Number Com suporte Com suporte Com suporte Com suporte
Int64 Number Com suporte Com suporte Sem suporte Sem suporte
Int64 BigInt Com suporte Com suporte Sem suporte Sem suporte
Single Number Com suporte Com suporte Com suporte Sem suporte
Double Number Com suporte Com suporte Com suporte Com suporte
IntPtr Number Com suporte Com suporte Com suporte Sem suporte
DateTime Date Com suporte Com suporte Sem suporte Sem suporte
DateTimeOffset Date Com suporte Com suporte Sem suporte Sem suporte
Exception Error Sem suporte Com suporte Com suporte Sem suporte
JSObject Object Sem suporte Com suporte Com suporte Com suporte
String String Sem suporte Com suporte Com suporte Com suporte
Object Any Sem suporte Com suporte Sem suporte Com suporte
Span<Byte> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Span<Int32> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Span<Double> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Byte> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Int32> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Double> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Task Promise Sem suporte Sem suporte Com suporte Sem suporte
Action Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1> Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1, T2> Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1, T2, T3> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, T2, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, T2, T3, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte

As seguintes condições se aplicam ao mapeamento de tipos e valores que sofrem realização de marshal:

  • A coluna Array of indica se o tipo .NET pode sofrer realização de marshal como um JSArray. Exemplo: C# int[] (Int32) mapeado para JSArray de Numbers.
  • Ao passar um valor JS para C# com um valor do tipo errado, a estrutura gera uma exceção na maioria dos casos. A estrutura não executa verificação de tipo em tempo de compilação no JS.
  • JSObject, Exception, Task e ArraySegment criam GCHandle e um proxy. Você pode disparar o descarte no código do desenvolvedor ou permitir que a coleta de lixo (GC, na sigla em inglês) do .NET descarte os objetos posteriormente. Esses tipos têm uma sobrecarga significativa de desempenho.
  • Array: o marshaling de uma matriz cria uma cópia da matriz no JS ou no .NET.
  • MemoryView
    • MemoryView é uma classe JS para o runtime do .NET WebAssembly realizar marshaling de Span e ArraySegment.
    • Ao contrário do marshaling de uma matriz, realizar marshaling de um Span ou ArraySegment não cria uma cópia da memória subjacente.
    • MemoryView só pode ter uma instância criada corretamente pelo runtime do WebAssembly do .NET. Portanto, não é possível importar uma função JS como um método .NET que tenha um parâmetro de Span ou ArraySegment.
    • MemoryView criado para um Span só é válido durante a chamada de interoperabilidade. Como Span é alocado na pilha de chamadas, que não persiste após a chamada de interoperabilidade, não é possível exportar um método .NET que retorna um Span.
    • MemoryView criado para um ArraySegment sobrevive após a chamada de interoperabilidade e é útil para compartilhar um buffer. Chamar dispose() em um MemoryView criado para um ArraySegment descarta o proxy e desafixa a matriz .NET subjacente. É recomendável chamar dispose() em um bloco try-finally para MemoryView.

A tabela a seguir indica os mapeamentos de tipo com suporte.

.NET JavaScript Nullable TaskparaPromise Opcional JSMarshalAs Array of
Boolean Boolean Com suporte Com suporte Com suporte Sem suporte
Byte Number Com suporte Com suporte Com suporte Com suporte
Char String Com suporte Com suporte Com suporte Sem suporte
Int16 Number Com suporte Com suporte Com suporte Sem suporte
Int32 Number Com suporte Com suporte Com suporte Com suporte
Int64 Number Com suporte Com suporte Sem suporte Sem suporte
Int64 BigInt Com suporte Com suporte Sem suporte Sem suporte
Single Number Com suporte Com suporte Com suporte Sem suporte
Double Number Com suporte Com suporte Com suporte Com suporte
IntPtr Number Com suporte Com suporte Com suporte Sem suporte
DateTime Date Com suporte Com suporte Sem suporte Sem suporte
DateTimeOffset Date Com suporte Com suporte Sem suporte Sem suporte
Exception Error Sem suporte Com suporte Com suporte Sem suporte
JSObject Object Sem suporte Com suporte Com suporte Com suporte
String String Sem suporte Com suporte Com suporte Com suporte
Object Any Sem suporte Com suporte Sem suporte Com suporte
Span<Byte> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Span<Int32> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Span<Double> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Byte> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Int32> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
ArraySegment<Double> MemoryView Sem suporte Sem suporte Sem suporte Sem suporte
Task Promise Sem suporte Sem suporte Com suporte Sem suporte
Action Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1> Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1, T2> Function Sem suporte Sem suporte Sem suporte Sem suporte
Action<T1, T2, T3> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, T2, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte
Func<T1, T2, T3, TResult> Function Sem suporte Sem suporte Sem suporte Sem suporte

As seguintes condições se aplicam ao mapeamento de tipos e valores que sofrem realização de marshal:

  • A coluna Array of indica se o tipo .NET pode sofrer realização de marshal como um JSArray. Exemplo: C# int[] (Int32) mapeado para JSArray de Numbers.
  • Ao passar um valor JS para C# com um valor do tipo errado, a estrutura gera uma exceção na maioria dos casos. A estrutura não executa verificação de tipo em tempo de compilação no JS.
  • JSObject, Exception, Task e ArraySegment criam GCHandle e um proxy. Você pode disparar o descarte no código do desenvolvedor ou permitir que a coleta de lixo (GC, na sigla em inglês) do .NET descarte os objetos posteriormente. Esses tipos têm uma sobrecarga significativa de desempenho.
  • Array: o marshaling de uma matriz cria uma cópia da matriz no JS ou no .NET.
  • MemoryView
    • MemoryView é uma classe JS para o runtime do .NET WebAssembly realizar marshaling de Span e ArraySegment.
    • Ao contrário do marshaling de uma matriz, realizar marshaling de um Span ou ArraySegment não cria uma cópia da memória subjacente.
    • MemoryView só pode ter uma instância criada corretamente pelo runtime do WebAssembly do .NET. Portanto, não é possível importar uma função JS como um método .NET que tenha um parâmetro de Span ou ArraySegment.
    • MemoryView criado para um Span só é válido durante a chamada de interoperabilidade. Como Span é alocado na pilha de chamadas, que não persiste após a chamada de interoperabilidade, não é possível exportar um método .NET que retorna um Span.
    • MemoryView criado para um ArraySegment sobrevive após a chamada de interoperabilidade e é útil para compartilhar um buffer. Chamar dispose() em um MemoryView criado para um ArraySegment descarta o proxy e desafixa a matriz .NET subjacente. É recomendável chamar dispose() em um bloco try-finally para MemoryView.

O nome do módulo [JSImport] no atributo e a chamada para carregar o módulo no componente com JSHost.ImportAsync devem corresponder e ser exclusivos no aplicativo. Ao criar uma biblioteca para implantação em um pacote NuGet, recomendamos usar o namespace do pacote NuGet como um prefixo em nomes de módulo. No exemplo a seguir, o nome do módulo reflete o pacote Contoso.InteropServices.JavaScript e uma pasta de classes de interoperabilidade de mensagens do usuário (UserMessages):

[JSImport("getMessage", 
    "Contoso.InteropServices.JavaScript.UserMessages.CallJavaScript1")]

As funções acessíveis no namespace global podem ser importadas usando o prefixo globalThis no nome da função e o atributo [JSImport] sem fornecer um nome de módulo. No exemplo a seguir, console.log é prefixado com globalThis. A função importada é chamada pelo método C# Log , que aceita uma mensagem de cadeia de caracteres C# (message) e realiza marshaling da cadeia de caracteres C# em um JSString para console.log:

[JSImport("globalThis.console.log")]
internal static partial void Log([JSMarshalAs<JSType.String>] string message);

Exporte scripts de um módulo JavaScript ES6 padrão posicionado com um componente ou com outros ativos estáticos JavaScript em um arquivo JS (por exemplo, wwwroot/js/{FILE NAME}.js, em que ativos estáticos JS são mantidos em uma pasta chamada js na pasta do aplicativo wwwroot e o espaço reservado {FILE NAME} é o nome do arquivo).

No exemplo a seguir, uma função JS chamada getMessage é exportada de um arquivo JS posicionado que retorna uma mensagem de boas-vindas, "Olá de Blazor!" em português:

CallJavaScript1.razor.js:

export function getMessage() {
  return 'Olá do Blazor!';
}

Chamar .NET do JavaScript

Esta seção explica como chamar métodos .NET de JS.

O componente CallDotNet1 a seguir chama JS que interage diretamente com o DOM para renderizar a cadeia de caracteres de mensagem de boas-vindas:

  • O CallDotNetmódulo JS é importado de forma assíncrona do arquivo JS posicionado para esse componente.
  • A função setMessageJS importada é chamada por SetWelcomeMessage.
  • A mensagem de boas-vindas retornada é exibida por setMessage na interface do usuário por meio do campo message.

Importante

No exemplo desta seção, a interoperabilidadeJS é usada para modificar um elemento DOM puramente para fins de demonstração depois que o componente é renderizado em OnAfterRender. Normalmente, você só deve alterar o DOM com JS quando o objeto não interage com Blazor. A abordagem mostrada nesta seção é semelhante aos casos em que uma biblioteca de terceiros JS é usada em um componente Razor, em que o componente interage com a biblioteca JS por meio de interoperabilidade JS, a biblioteca de terceiros JS interage com parte do DOM e Blazor não está envolvida diretamente nas atualizações do DOM para essa parte do DOM. Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).

CallDotNet1.razor:

@page "/call-dotnet-1"
@rendermode InteractiveWebAssembly
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call .NET Example 1)
</h1>

<p>
    <span id="result">.NET method not executed yet</span>
</p>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSHost.ImportAsync("CallDotNet1", 
                "../Components/Pages/CallDotNet1.razor.js");

            SetWelcomeMessage();
        }
    }
}
@page "/call-dotnet-1"
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call .NET Example 1)
</h1>

<p>
    <span id="result">.NET method not executed yet</span>
</p>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSHost.ImportAsync("CallDotNet1", 
                "../Pages/CallDotNet1.razor.js");

            SetWelcomeMessage();
        }
    }
}

Para exportar um método .NET para que ele possa ser chamado de JS, use o atributo [JSExport] .

No exemplo a seguir:

  • SetWelcomeMessage chama uma função JS nomeada setMessage. A função JS chama o .NET para receber a mensagem de boas-vindas de GetMessageFromDotnet e exibe a mensagem na interface do usuário.
  • GetMessageFromDotnet é um método .NET com o atributo [JSExport] que retorna uma mensagem de boas-vindas, "Olá de Blazor!" em português.

CallDotNet1.razor.cs:

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Components.Pages;

[SupportedOSPlatform("browser")]
public partial class CallDotNet1
{
    [JSImport("setMessage", "CallDotNet1")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

O namespace do aplicativo para a classe parcial CallDotNet1 anterior é BlazorSample. O namespace do componente será BlazorSample.Components.Pages. Se estiver usando o componente anterior em um aplicativo de teste local, atualize o namespace do aplicativo para corresponder ao aplicativo. Por exemplo, o namespace do componente será ContosoApp.Components.Pages se o namespace do aplicativo for ContosoApp. Para saber mais, consulte Componentes Razor do ASP.NET Core.

No exemplo a seguir, uma função JS nomeada setMessage é importada de um arquivo JS posicionado.

O método setMessage:

  • Chamadas globalThis.getDotnetRuntime(0) para expor a instância de runtime do .NET do WebAssembly para chamar métodos .NET exportados.
  • Obtém as exportações do assembly do aplicativo JS. O nome do assembly do aplicativo no exemplo a seguir é BlazorSample.
  • Chama o método BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet das exportações (exports). O valor retornado, que é a mensagem de boas-vindas, é atribuído ao texto CallDotNet1 do componente <span>. O namespace do aplicativo é BlazorSample e o namespace do componente CallDotNet1 é BlazorSample.Components.Pages.

CallDotNet1.razor.js:

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText = 
    exports.BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet();
}
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Pages;

[SupportedOSPlatform("browser")]
public partial class CallDotNet1
{
    [JSImport("setMessage", "CallDotNet1")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

O namespace do aplicativo para a classe parcial CallDotNet1 anterior é BlazorSample. O namespace do componente será BlazorSample.Pages. Se estiver usando o componente anterior em um aplicativo de teste local, atualize o namespace do aplicativo para corresponder ao aplicativo. Por exemplo, o namespace do componente será ContosoApp.Pages se o namespace do aplicativo for ContosoApp. Para saber mais, consulte Componentes Razor do ASP.NET Core.

No exemplo a seguir, uma função JS nomeada setMessage é importada de um arquivo JS posicionado.

O método setMessage:

  • Chamadas globalThis.getDotnetRuntime(0) para expor a instância de runtime do .NET do WebAssembly para chamar métodos .NET exportados.
  • Obtém as exportações do assembly do aplicativo JS. O nome do assembly do aplicativo no exemplo a seguir é BlazorSample.
  • Chama o método BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet das exportações (exports). O valor retornado, que é a mensagem de boas-vindas, é atribuído ao texto CallDotNet1 do componente <span>. O namespace do aplicativo é BlazorSample e o namespace do componente CallDotNet1 é BlazorSample.Pages.

CallDotNet1.razor.js:

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText = 
    exports.BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet();
}

Observação

A chamada de getAssemblyExports para obter as exportações pode ocorrer em um inicializador JavaScript para disponibilidade em todo o aplicativo.

Várias chamadas de importação de módulo

Depois que um módulo JS é carregado, as funções do módulo ficam disponíveis para os componentes e classes do aplicativo, desde que o aplicativo esteja em execução na janela ou guia do navegador JS sem que o usuário recarregue o aplicativo manualmente. JSHost.ImportAsync pode ser chamado várias vezes no mesmo módulo sem uma penalidade de desempenho significativa quando:

  • O usuário visita um componente que chama JSHost.ImportAsync para importar um módulo, navega para longe do componente e retorna para o componente em que JSHost.ImportAsync é chamado novamente para a mesma importação de módulo.
  • O mesmo módulo é usado por diferentes componentes e carregado por JSHost.ImportAsync em cada um dos componentes.

Uso de um único módulo JavaScript entre componentes

Antes de seguir as diretrizes nesta seção, leia as seções Chamar JavaScript do .NET e Chamar .NET do JavaScript deste artigo, que fornecem diretrizes gerais sobre [JSImport]/interoperabilidade [JSExport].

O exemplo nesta seção mostra como usar a interoperabilidade JS de um módulo JS compartilhado em um aplicativo do lado do cliente. As diretrizes nesta seção não se aplicam a RCLs (bibliotecas de classes) Razor.

Os componentes, classes, métodos C# e funções JS a seguir são usados:

  • Classe Interop (Interop.cs): configura a interoperabilidade JS de importação e exportação com os atributos [JSImport] e [JSExport] para um módulo nomeado Interop.
    • GetWelcomeMessage: método .NET que chama a função getMessageJS importada.
    • SetWelcomeMessage: método .NET que chama a função setMessageJS importada.
    • GetMessageFromDotnet: um método C# exportado que retorna uma cadeia de caracteres de mensagem de boas-vindas quando chamado de JS.
  • Arquivo wwwroot/js/interop.js: contém as funções JS.
    • getMessage: retorna uma mensagem de boas-vindas quando chamada pelo código C# em um componente.
    • setMessage: chama o método GetMessageFromDotnet C# e atribui a mensagem de boas-vindas retornada a um elemento <span> DOM.
  • Program.cs chama JSHost.ImportAsync para carregar o módulo de wwwroot/js/interop.js.
  • Componente CallJavaScript2 (CallJavaScript2.razor): chama GetWelcomeMessage e exibe a mensagem de boas-vindas retornada na interface do usuário do componente.
  • Componente CallDotNet2 (CallDotNet2.razor): chama SetWelcomeMessage.

Interop.cs:

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.JavaScriptInterop;

[SupportedOSPlatform("browser")]
public partial class Interop
{
    [JSImport("getMessage", "Interop")]
    internal static partial string GetWelcomeMessage();

    [JSImport("setMessage", "Interop")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

No exemplo anterior, o namespace do aplicativo é BlazorSamplee o namespace completo das classes de interoperabilidade C# é BlazorSample.JavaScriptInterop.

wwwroot/js/interop.js:

export function getMessage() {
  return 'Olá do Blazor!';
}

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText =
    exports.BlazorSample.JavaScriptInterop.Interop.GetMessageFromDotnet();
}

Disponibilize o namespace System.Runtime.InteropServices.JavaScript na parte superior do arquivo Program.cs:

using System.Runtime.InteropServices.JavaScript;

Carregue o módulo em Program.cs antes de WebAssemblyHost.RunAsync ser chamado:

if (OperatingSystem.IsBrowser())
{
    await JSHost.ImportAsync("Interop", "../js/interop.js");
}

CallJavaScript2.razor:

@page "/call-javascript-2"
@rendermode InteractiveWebAssembly
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 2)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = Interop.GetWelcomeMessage();
    }
}
@page "/call-javascript-2"
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 2)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = Interop.GetWelcomeMessage();
    }
}

CallDotNet2.razor:

@page "/call-dotnet-2"
@rendermode InteractiveWebAssembly
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop  
    (Call .NET Example 2)
</h1>

<p>
    <span id="result">.NET method not executed</span>
</p>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Interop.SetWelcomeMessage();
        }
    }
}
@page "/call-dotnet-2"
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop  
    (Call .NET Example 2)
</h1>

<p>
    <span id="result">.NET method not executed</span>
</p>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Interop.SetWelcomeMessage();
        }
    }
}

Importante

No exemplo desta seção, a interoperabilidadeJS é usada para modificar um elemento DOM puramente para fins de demonstração depois que o componente é renderizado em OnAfterRender. Normalmente, você só deve alterar o DOM com JS quando o objeto não interage com Blazor. A abordagem mostrada nesta seção é semelhante aos casos em que uma biblioteca de terceiros JS é usada em um componente Razor, em que o componente interage com a biblioteca JS por meio de interoperabilidade JS, a biblioteca de terceiros JS interage com parte do DOM e Blazor não está envolvida diretamente nas atualizações do DOM para essa parte do DOM. Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).

Recursos adicionais