Interopérabilité [JSImport]/[JSExport] JavaScript avec ASP.NET Core Blazor

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

Pour la version actuelle, consultez la version .NET 8 de cet article.

Cet article explique comment interagir avec JavaScript (JS) dans les composants côté client en utilisant l’API d’interopérabilité JavaScript (JS) [JSImport]/[JSExport] publiée pour les applications adoptant .NET 7 (ou une version ultérieure).

Blazor fournit son propre mécanisme d’interopérabilité JS basé sur l’interface IJSRuntime. L’interopérabilité JS de Blazor est uniformément prise en charge dans les modes de rendu Blazor et pour les applications Blazor Hybrid. IJSRuntime permet également aux auteurs de bibliothèques de créer des bibliothèques d’interopérabilité JS pour les partager dans l’écosystème Blazor, ce qui est toujours l’approche recommandée pour l’interopérabilité JS dans Blazor. Voir les articles suivants :

Cet article décrit une autre approche d’interopérabilité JS spécifique aux composants côté client exécutés sur WebAssembly. Ces approches sont appropriées lorsque vous comptez uniquement exécuter le code dans un environnement WebAssembly côté client. Les auteurs de bibliothèque peuvent utiliser ces approches pour optimiser l’interopérabilité JS en vérifiant, lors de l’exécution du code, si l’application s’exécute sur WebAssembly dans un navigateur (OperatingSystem.IsBrowser). Les approches décrites dans cet article doivent être utilisées pour remplacer l’API d’interopérabilité JS démarshalée obsolète lors de la migration vers .NET 7 (ou une version ultérieure).

Remarque

Cet article se concentre sur l’interopérabilité JS dans les composants côté client. Pour obtenir des conseils sur l’appel de .NET dans les applications JavaScript, consultez Exécuter .NET à partir de JavaScript.

API d’interopérabilité JavaScript obsolète

L’interopérabilité JS démarshalée avec l’API IJSUnmarshalledRuntime est obsolète dans ASP.NET Core dans .NET 7 ou une version ultérieure. Suivez les instructions de cet article pour remplacer l’API obsolète.

Prérequis

Téléchargez et installez .NET 7 ou une version plus récente, s’il n’est pas déjà installé sur le système ou si la dernière version n’est pas installée sur le système.

Espace de noms

L’API d’interopérabilité JS décrite dans cet article est contrôlée par des attributs de l’espace de noms System.Runtime.InteropServices.JavaScript.

Activer les blocs non sécurisés

Activez la propriété AllowUnsafeBlocks dans le fichier projet de l’application, ce qui permet au générateur de code dans le compilateur Roslyn d’utiliser des pointeurs pour l’interopérabilité JS :

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

Avertissement

L’API d’interopérabilité JS nécessite l’activation de AllowUnsafeBlocks. Soyez vigilant quand vous implémentez votre propre code non sécurisé dans des applications .NET, car cela peut présenter des risques pour la sécurité et la stabilité. Pour plus d’informations, consultez Code non sécurisé, types de pointeur et pointeurs de fonction.

Appeler JavaScript à partir de .NET

Cette section explique comment appeler des fonctions JS à partir de .NET.

Dans le composant CallJavaScript1 suivant :

  • Le module CallJavaScript1 est importé de manière asynchrone à partir du fichier JS colocalisé avec JSHost.ImportAsync.
  • La fonction getMessageJS importée est appelée par GetWelcomeMessage.
  • La chaîne de message d’accueil retournée s’affiche dans l’interface utilisateur via le champ 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();
    }
}

Remarque

Incluez un code de vérification conditionnel avec OperatingSystem.IsBrowser pour vous assurer que l’interopérabilité JS est appelée uniquement par un composant rendu sur le client. Cela est important pour les bibliothèques/packages NuGet qui ciblent les composants côté serveur ne pouvant pas exécuter le code fourni par cette API d’interopérabilité JS.

Pour importer une fonction JS pour l’appeler à partir de C#, utilisez l’attribut [JSImport] sur une signature de méthode C# qui correspond à la signature de la fonction JS. Le premier paramètre de l’attribut [JSImport] est le nom de la fonction JS à importer, et le deuxième paramètre est le nom du module JS.

Dans l’exemple suivant, getMessage est une fonction JS qui retourne un string pour un module nommé CallJavaScript1. La signature de méthode C# correspond : aucun paramètre n’est passé à la fonction JS, et la fonction JS retourne un string. La fonction JS est appelée par GetWelcomeMessage dans le code 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();
}

L’espace de noms de l’application pour la classe partielle CallJavaScript1 précédente est BlazorSample. L’espace de noms du composant est BlazorSample.Components.Pages. Si vous utilisez le composant précédent dans une application de test locale, mettez à jour l’espace de noms pour qu’il corresponde à l’application. Par exemple, l’espace de noms est ContosoApp.Components.Pages si l’espace de noms de l’application est ContosoApp. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

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();
}

L’espace de noms de l’application pour la classe partielle CallJavaScript1 précédente est BlazorSample. L’espace de noms du composant est BlazorSample.Pages. Si vous utilisez le composant précédent dans une application de test locale, mettez à jour l’espace de noms pour qu’il corresponde à l’application. Par exemple, l’espace de noms est ContosoApp.Pages si l’espace de noms de l’application est ContosoApp. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Dans la signature de méthode importée, vous pouvez utiliser des types .NET pour les paramètres et les valeurs de retour, qui sont marshalés automatiquement par le runtime. Utilisez JSMarshalAsAttribute<T> pour contrôler la façon dont les paramètres de méthode importés sont marshalés. Par exemple, vous pouvez choisir de marshaler long en tant que System.Runtime.InteropServices.JavaScript.JSType.Number ou System.Runtime.InteropServices.JavaScript.JSType.BigInt. Vous pouvez passer des rappels Action/Func<TResult> en tant que paramètres, qui sont marshalés en tant que fonctions JS pouvant être appelées. Vous pouvez passer à la fois des références d’objets JS et des références d’objets managés. Elles sont marshalées en tant qu’objets proxy, ce qui permet de conserver l’objet actif entre les environnements jusqu’à ce que le proxy soit traité pour un nettoyage de la mémoire. Vous pouvez également importer et exporter des méthodes asynchrones avec un résultat Task, qui sont marshalées en tant que promesses JS. La plupart des types marshalés fonctionnent dans les deux sens, en tant que paramètres et valeurs de retour, sur les méthodes importées et exportées, qui sont couvertes dans la section Appeler .NET à partir de JavaScript plus loin dans cet article.

Le tableau suivant indique les mappages de types pris en charge.

.NET JavaScript Nullable TaskàPromise JSMarshalAs facultatif Array of
Boolean Boolean Pris en charge Pris en charge Pris en charge Non pris en charge
Byte Number Pris en charge Pris en charge Pris en charge Pris en charge
Char String Pris en charge Pris en charge Pris en charge Non pris en charge
Int16 Number Pris en charge Pris en charge Pris en charge Non pris en charge
Int32 Number Pris en charge Pris en charge Pris en charge Pris en charge
Int64 Number Pris en charge Pris en charge Non pris en charge Non pris en charge
Int64 BigInt Pris en charge Pris en charge Non pris en charge Non pris en charge
Single Number Pris en charge Pris en charge Pris en charge Non pris en charge
Double Number Pris en charge Pris en charge Pris en charge Pris en charge
IntPtr Number Pris en charge Pris en charge Pris en charge Non pris en charge
DateTime Date Pris en charge Pris en charge Non pris en charge Non pris en charge
DateTimeOffset Date Pris en charge Pris en charge Non pris en charge Non pris en charge
Exception Error Non pris en charge Pris en charge Pris en charge Non pris en charge
JSObject Object Non pris en charge Pris en charge Pris en charge Pris en charge
String String Non pris en charge Pris en charge Pris en charge Pris en charge
Object Any Non pris en charge Pris en charge Non pris en charge Pris en charge
Span<Byte> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Span<Int32> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Span<Double> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Byte> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Int32> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Double> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Task Promise Non pris en charge Non pris en charge Pris en charge Non pris en charge
Action Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1, T2> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1, T2, T3> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, T2, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, T2, T3, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge

Les conditions suivantes s’appliquent au mappage de type et aux valeurs marshalées :

  • La colonne Array of indique si le type .NET peut être marshalé en tant que JSArray. Exemple : C# int[] (Int32) mappé à JSArray de Number.
  • Quand une valeur JS est passée en C# avec un type incorrect, le framework lève une exception dans la plupart des cas. Le framework n’effectue pas de contrôle de type au moment de la compilation en JS.
  • JSObject, Exception, Task et ArraySegment créent GCHandle et un proxy. Vous pouvez déclencher la suppression dans le code de développeur, ou autoriser le GC (nettoyage de la mémoire) .NET à supprimer les objets plus tard. Ces types entraînent une surcharge importante au niveau des performances.
  • Array : Le marshaling d’un tableau crée une copie du tableau en JS ou dans .NET.
  • MemoryView
    • MemoryView est une classe JS qui permet au runtime .NET WebAssembly de marshaler Span et ArraySegment.
    • Contrairement au marshaling d’un tableau, le marshaling de Span ou ArraySegment ne crée pas de copie de la mémoire sous-jacente.
    • MemoryView peut uniquement être correctement instancié par le runtime .NET WebAssembly. Il n’est donc pas possible d’importer une fonction JS en tant que méthode .NET qui a un paramètre Span ou ArraySegment.
    • Un MemoryView créé pour un Span est uniquement valide pour la durée de l’appel d’interopérabilité. Dans la mesure où Span est alloué sur la pile des appels, qui ne persiste pas après l’appel d’interopérabilité, il n’est pas possible d’exporter une méthode .NET qui retourne Span.
    • Un MemoryView créé pour un ArraySegment survit après l’appel d’interopérabilité, et est utile pour partager de la mémoire tampon. L’appel de dispose() sur un MemoryView créé pour ArraySegment supprime le proxy, et dissocie le tableau .NET sous-jacent. Nous vous recommandons d’appeler dispose() dans un bloc try-finally pour MemoryView.

Le tableau suivant indique les mappages de types pris en charge.

.NET JavaScript Nullable TaskàPromise JSMarshalAs facultatif Array of
Boolean Boolean Pris en charge Pris en charge Pris en charge Non pris en charge
Byte Number Pris en charge Pris en charge Pris en charge Pris en charge
Char String Pris en charge Pris en charge Pris en charge Non pris en charge
Int16 Number Pris en charge Pris en charge Pris en charge Non pris en charge
Int32 Number Pris en charge Pris en charge Pris en charge Pris en charge
Int64 Number Pris en charge Pris en charge Non pris en charge Non pris en charge
Int64 BigInt Pris en charge Pris en charge Non pris en charge Non pris en charge
Single Number Pris en charge Pris en charge Pris en charge Non pris en charge
Double Number Pris en charge Pris en charge Pris en charge Pris en charge
IntPtr Number Pris en charge Pris en charge Pris en charge Non pris en charge
DateTime Date Pris en charge Pris en charge Non pris en charge Non pris en charge
DateTimeOffset Date Pris en charge Pris en charge Non pris en charge Non pris en charge
Exception Error Non pris en charge Pris en charge Pris en charge Non pris en charge
JSObject Object Non pris en charge Pris en charge Pris en charge Pris en charge
String String Non pris en charge Pris en charge Pris en charge Pris en charge
Object Any Non pris en charge Pris en charge Non pris en charge Pris en charge
Span<Byte> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Span<Int32> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Span<Double> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Byte> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Int32> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
ArraySegment<Double> MemoryView Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Task Promise Non pris en charge Non pris en charge Pris en charge Non pris en charge
Action Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1, T2> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Action<T1, T2, T3> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, T2, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge
Func<T1, T2, T3, TResult> Function Non pris en charge Non pris en charge Non pris en charge Non pris en charge

Les conditions suivantes s’appliquent au mappage de type et aux valeurs marshalées :

  • La colonne Array of indique si le type .NET peut être marshalé en tant que JSArray. Exemple : C# int[] (Int32) mappé à JSArray de Number.
  • Quand une valeur JS est passée en C# avec un type incorrect, le framework lève une exception dans la plupart des cas. Le framework n’effectue pas de contrôle de type au moment de la compilation en JS.
  • JSObject, Exception, Task et ArraySegment créent GCHandle et un proxy. Vous pouvez déclencher la suppression dans le code de développeur, ou autoriser le GC (nettoyage de la mémoire) .NET à supprimer les objets plus tard. Ces types entraînent une surcharge importante au niveau des performances.
  • Array : Le marshaling d’un tableau crée une copie du tableau en JS ou dans .NET.
  • MemoryView
    • MemoryView est une classe JS qui permet au runtime .NET WebAssembly de marshaler Span et ArraySegment.
    • Contrairement au marshaling d’un tableau, le marshaling de Span ou ArraySegment ne crée pas de copie de la mémoire sous-jacente.
    • MemoryView peut uniquement être correctement instancié par le runtime .NET WebAssembly. Il n’est donc pas possible d’importer une fonction JS en tant que méthode .NET qui a un paramètre Span ou ArraySegment.
    • Un MemoryView créé pour un Span est uniquement valide pour la durée de l’appel d’interopérabilité. Dans la mesure où Span est alloué sur la pile des appels, qui ne persiste pas après l’appel d’interopérabilité, il n’est pas possible d’exporter une méthode .NET qui retourne Span.
    • Un MemoryView créé pour un ArraySegment survit après l’appel d’interopérabilité, et est utile pour partager de la mémoire tampon. L’appel de dispose() sur un MemoryView créé pour ArraySegment supprime le proxy, et dissocie le tableau .NET sous-jacent. Nous vous recommandons d’appeler dispose() dans un bloc try-finally pour MemoryView.

Le nom du module dans l’attribut [JSImport] et l’appel pour charger le module dans le composant avec JSHost.ImportAsync doivent correspondre et être uniques dans l’application. Lors de la création d’une bibliothèque pour le déploiement dans un package NuGet, nous vous recommandons d’utiliser l’espace de noms de package NuGet comme préfixe dans les noms de modules. Dans l’exemple suivant, le nom du module reflète le package Contoso.InteropServices.JavaScript et un dossier de classes d’interopérabilité des messages utilisateur (UserMessages) :

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

Les fonctions accessibles sur l’espace de noms global peuvent être importées à l’aide du préfixe globalThis dans le nom de la fonction et à l’aide de l’attribut [JSImport] sans fournir de nom de module. Dans l’exemple suivant, console.log est précédé de globalThis. La fonction importée est appelée par la méthode C# Log, qui accepte un message de chaîne C# (message) et marshale la chaîne C# en JSString pour console.log :

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

Exportez des scripts à partir d’un module JavaScript ES6 standard colocalisé avec un composant ou placé avec d’autres ressources statiques JavaScript dans un fichier JS (par exemple, wwwroot/js/{FILE NAME}.js, où des ressources statiques JS sont conservées dans un dossier nommé js dans le dossier wwwroot de l’application, et où l’espace réservé {FILE NAME} est le nom de fichier).

Dans l’exemple suivant, une fonction JS nommée getMessage est exportée à partir d’un fichier JS colocalisé qui retourne un message de bienvenue, « Hello from Blazor! » en portugais :

CallJavaScript1.razor.js:

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

Appeler .NET à partir de JavaScript

Cette section explique comment appeler des méthodes .NET à partir de JS.

Le composant CallDotNet1 suivant appelle JS, qui interagit directement avec le DOM pour afficher la chaîne de message d’accueil :

  • Le CallDotNetmodule JS est importé de manière asynchrone à partir du fichier JS colocalisé pour ce composant.
  • La fonction setMessageJS importée est appelée par SetWelcomeMessage.
  • Le message de bienvenue retourné est affiché par setMessage dans l’interface utilisateur via le champ message.

Important

Dans l’exemple de cette section, l’interopérabilité JS est utilisée pour muter un élément DOM uniquement à des fins de démonstration après le rendu du composant dans OnAfterRender. En règle générale, vous devez uniquement muter le DOM avec JS lorsque l’objet n’interagit pas avec Blazor. L’approche décrite dans cette section est similaire aux cas où une bibliothèque tierce JS est utilisée dans un composant Razor, où le composant interagit avec la bibliothèque JS via l’interopérabilité JS, la bibliothèque JS tierce interagit avec une partie du DOM et Blazor n’est pas impliqué directement avec les mises à jour DOM de cette partie du DOM. Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité 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();
        }
    }
}

Pour exporter une méthode .NET afin qu’elle puisse être appelée à partir de JS, utilisez l’attribut [JSExport].

Dans l’exemple suivant :

  • SetWelcomeMessage appelle une fonction JS nommée setMessage. La fonction JS appelle .NET pour recevoir le message d’accueil de GetMessageFromDotnet et affiche le message dans l’interface utilisateur.
  • GetMessageFromDotnet est une méthode .NET avec l’attribut [JSExport], qui retourne un message de bienvenue, « Hello from Blazor! » en portugais.

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!";
    }
}

L’espace de noms de l’application pour la classe partielle CallDotNet1 précédente est BlazorSample. L’espace de noms du composant est BlazorSample.Components.Pages. Si vous utilisez le composant précédent dans une application de test locale, mettez à jour l’espace de noms de l’application pour qu’il corresponde à l’application. Par exemple, l’espace de noms du composant est ContosoApp.Components.Pages si l’espace de noms de l’application est ContosoApp. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Dans l’exemple suivant, une fonction JS nommée setMessage est importée à partir d’un fichier JS colocalisé.

La méthode setMessage :

  • Appelle globalThis.getDotnetRuntime(0) pour exposer l’instance de runtime WebAssembly .NET pour appeler des méthodes .NET exportées.
  • Obtient les exportations JS de l’assembly d’application. Le nom de l’assembly d’application dans l’exemple suivant est BlazorSample.
  • Appelle la méthode BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet à partir des exportations (exports). La valeur retournée, qui est le message d’accueil, est affectée au texte <span> du composant CallDotNet1. L’espace de noms de l’application est BlazorSample, et l’espace de noms du composant CallDotNet1 est 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!";
    }
}

L’espace de noms de l’application pour la classe partielle CallDotNet1 précédente est BlazorSample. L’espace de noms du composant est BlazorSample.Pages. Si vous utilisez le composant précédent dans une application de test locale, mettez à jour l’espace de noms de l’application pour qu’il corresponde à l’application. Par exemple, l’espace de noms du composant est ContosoApp.Pages si l’espace de noms de l’application est ContosoApp. Pour plus d’informations, consultez Composants ASP.NET Core Razor.

Dans l’exemple suivant, une fonction JS nommée setMessage est importée à partir d’un fichier JS colocalisé.

La méthode setMessage :

  • Appelle globalThis.getDotnetRuntime(0) pour exposer l’instance de runtime WebAssembly .NET pour appeler des méthodes .NET exportées.
  • Obtient les exportations JS de l’assembly d’application. Le nom de l’assembly d’application dans l’exemple suivant est BlazorSample.
  • Appelle la méthode BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet à partir des exportations (exports). La valeur retournée, qui est le message d’accueil, est affectée au texte <span> du composant CallDotNet1. L’espace de noms de l’application est BlazorSample, et l’espace de noms du composant CallDotNet1 est 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();
}

Remarque

L’appel de getAssemblyExports pour obtenir les exportations peut se produire dans un initialiseur JavaScript pour la disponibilité dans l’application.

Appels d’importation de plusieurs modules

Une fois qu’un module JS est chargé, les fonctions JS du module sont disponibles pour les composants et classes de l’application tant que l’application s’exécute dans la fenêtre ou l’onglet du navigateur, sans que l’utilisateur recharge manuellement l’application. JSHost.ImportAsync peut être appelé plusieurs fois sur le même module sans pénalité de performances significative lorsque :

  • L’utilisateur visite un composant qui appelle JSHost.ImportAsync pour importer un module, s’éloigne du composant, puis retourne au composant, où JSHost.ImportAsync est appelé à nouveau pour la même importation de module.
  • Le même module est utilisé par différents composants et chargé par JSHost.ImportAsync dans chacun des composants.

Utilisation d’un seul module JavaScript entre les composants

Avant de suivre les instructions de cette section, lisez les sections Appeler JavaScript à partir de .NET et Appeler .NET à partir de JavaScript de cet article, qui fournissent des conseils généraux sur l’interopérabilité [JSImport]/[JSExport].

L’exemple de cette section montre comment utiliser l’interopérabilité JS à partir d’un module JS partagé dans une application côté client. Les instructions fournies dans cette section ne s’appliquent pas aux bibliothèques de classes Razor.

Les composants, classes, méthodes C# et fonctions JS qui suivent sont utilisés :

  • Classe Interop (Interop.cs) : configure l’interopérabilité JS d’importation et d’exportation avec les attributs [JSImport] et [JSExport] pour un module nommé Interop.
    • GetWelcomeMessage : méthode .NET qui appelle la fonction getMessageJS importée.
    • SetWelcomeMessage : méthode .NET qui appelle la fonction setMessageJS importée.
    • GetMessageFromDotnet : méthode C# exportée qui retourne une chaîne de message d’accueil lorsqu’elle est appelée à partir de JS.
  • Fichier wwwroot/js/interop.js : contient les fonctions JS.
    • getMessage : retourne un message de bienvenue lorsqu’il est appelé par du code C# dans un composant.
    • setMessage : appelle la GetMessageFromDotnet méthode C# et affecte le message d’accueil retourné à un élément <span> DOM.
  • Program.cs appelle JSHost.ImportAsync pour charger le module à partir de wwwroot/js/interop.js.
  • Composant CallJavaScript2 (CallJavaScript2.razor) : appelle GetWelcomeMessage et affiche le message de bienvenue retourné dans l’interface utilisateur du composant.
  • Composant CallDotNet2 (CallDotNet2.razor) : appelle 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!";
    }
}

Dans l’exemple précédent, l’espace de noms de l’application est BlazorSample, et l’espace de noms complet pour les classes d’interopérabilité C# est 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();
}

Rendez l’espace de noms System.Runtime.InteropServices.JavaScript disponible en haut du fichier Program.cs :

using System.Runtime.InteropServices.JavaScript;

Chargez le module dans Program.cs avant l’appel à WebAssemblyHost.RunAsync :

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();
        }
    }
}

Important

Dans l’exemple de cette section, l’interopérabilité JS est utilisée pour muter un élément DOM uniquement à des fins de démonstration après le rendu du composant dans OnAfterRender. En règle générale, vous devez uniquement muter le DOM avec JS lorsque l’objet n’interagit pas avec Blazor. L’approche décrite dans cette section est similaire aux cas où une bibliothèque tierce JS est utilisée dans un composant Razor, où le composant interagit avec la bibliothèque JS via l’interopérabilité JS, la bibliothèque JS tierce interagit avec une partie du DOM et Blazor n’est pas impliqué directement avec les mises à jour DOM de cette partie du DOM. Pour plus d’informations, consultez Interopérabilité JavaScript et ASP.NET Core Blazor (interopérabilité JS).

Ressources supplémentaires