JavaScript [JSImport]
/[JSExport]
interop
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. 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 exécuter .NET à partir de JavaScript (JS) en utilisant l’interopérabilité JS[JSImport]
/[JSExport]
.
Pour obtenir des conseils supplémentaires, consultez les instructions relatives à la configuration et à l’hébergement d’applications WebAssembly .NET dans le référentiel GitHub .NET Runtime (dotnet/runtime
).
Les applications JS existantes peuvent tirer parti de la prise en charge étendue de WebAssembly côté client pour réutiliser les bibliothèques .NET à partir de JS, ou pour créer des applications et des frameworks basés sur .NET.
Remarque
Cet article se concentre sur l’exécution de .NET à partir d’applications JS sans aucune dépendance par rapport à Blazor. Pour obtenir des instructions sur l’utilisation de l’interopérabilité [JSImport]
/[JSExport]
dans les applications Blazor WebAssembly, consultez Interopérabilité JavaScript JSImport/JSExport avec ASP.NET Core Blazor.
Ces approches sont appropriées quand vous comptez uniquement exécuter le code dans un environnement WebAssembly (WASM). Les bibliothèques peuvent effectuer une vérification au moment de l’exécution pour déterminer si l’application s’exécute sur WASM en appelant OperatingSystem.IsBrowser.
Prérequis
Kit de développement logiciel (SDK) .NET (dernière version)
Installez la charge de travail wasm-tools
, qui intègre les cibles MSBuild associées, dans un interpréteur de commandes d’administration :
dotnet workload install wasm-tools
Les outils peuvent également être installés via le programme d’installation de Visual Studio sous la charge de travail Développement web et ASP.NET dans le programme d’installation de Visual Studio. Sélectionnez l’option Outils de construction .NET WebAssembly dans la liste des composants facultatifs.
Si vous le souhaitez, vous pouvez installer la charge de travail wasm-experimental
, qui contient des modèles de projet expérimentaux pour bien démarrer avec .NET sur WebAssembly dans une application de navigateur (application de navigateur WebAssembly) ou dans une application de console basée sur Node.js (application de console WebAssembly). Cette charge de travail n’est pas nécessaire si vous comptez intégrer l’interopérabilité JS[JSImport]
/[JSExport]
dans une application JS existante.
dotnet workload install wasm-experimental
Les modèles peuvent également être installés à partir du package NuGet Microsoft.NET.Runtime.WebAssembly.Templates
avec la commande suivante :
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Pour plus d’informations, consultez la section Modèles de charge de travail et de projet expérimentaux.
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.
Configuration du projet
Pour configurer un projet (.csproj
) afin d’activer l’interopérabilité JS :
Configurez le moniker de framework cible (espace réservé
{TARGET FRAMEWORK}
) :<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
.NET 7 (
net7.0
) ou version ultérieure est pris en charge.Activez la propriété AllowUnsafeBlocks, qui permet au générateur de code du compilateur Roslyn d’utiliser des pointeurs pour l’interopérabilité JS :
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
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.
Voici un exemple de fichier projet (.csproj
) après la configuration. L’espace réservé {TARGET FRAMEWORK}
est le framework cible :
<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
<PropertyGroup>
<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Définissez le moniker de framework cible :
<TargetFramework>net7.0</TargetFramework>
.NET 7 (
net7.0
) ou version ultérieure est pris en charge.Spécifiez
browser-wasm
pour l’identificateur de runtime :<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
Spécifiez un type de sortie d’exécutable :
<OutputType>Exe</OutputType>
Activez la propriété AllowUnsafeBlocks, qui permet au générateur de code du compilateur Roslyn d’utiliser des pointeurs pour l’interopérabilité JS :
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
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.
Spécifiez
WasmMainJSPath
pour pointer vers un fichier sur le disque. Ce fichier est publié avec l’application, mais son utilisation n’est pas nécessaire si vous intégrez .NET à une application JS existante.Dans l’exemple suivant, le fichier JS situé sur le disque est
main.js
, mais tout nom de fichier JS est autorisé :<WasmMainJSPath>main.js</WasmMainJSPath>
Exemple de fichier projet (.csproj
) après la configuration :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WasmMainJSPath>main.js</WasmMainJSPath>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Interopérabilité JavaScript sur WASM
Les API de l’exemple suivant sont importées à partir de dotnet.js
. Ces API vous permettent de configurer des modules nommés qui peuvent être importés dans votre code C#, et d’appeler des méthodes exposées par votre code .NET, notamment Program.Main
.
Important
« Importer » et « exporter » sont définis tout au long de cet article du point de vue de .NET :
- Une application importe des méthodes JS pour qu’elles puissent être appelées à partir de .NET.
- L’application exporte des méthodes .NET pour qu’elles puissent être appelées à partir de JS.
Dans l’exemple suivant :
Le fichier
dotnet.js
permet de créer et de démarrer le runtime .NET WebAssembly.dotnet.js
est généré dans le cadre de la sortie de build de l’application.Important
Pour une intégration à une application existante, copiez le contenu du dossier de sortie de publication† vers les ressources de déploiement de l’application existante pour qu’il soit mis à disposition avec le rest de l’application. Pour les déploiements en production, publiez l’application avec la commande
dotnet publish -c Release
dans un interpréteur de commandes, puis déployez le contenu du dossier de sortie avec l’application.† Le dossier de sortie de publication est l’emplacement cible de votre profil de publication. La valeur par défaut pour un profil Release dans .NET 8 ou version ultérieure est
bin/Release/{TARGET FRAMEWORK}/publish
, où l’espace réservé{TARGET FRAMEWORK}
est le framework cible (par exemple,net8.0
).dotnet.create()
configure le runtime .NET WebAssembly.
setModuleImports
associe un nom à un module de fonctions JS à des fins d’importation dans .NET. Le module JS contient une fonctiondom.setInnerText
, qui accepte un sélectionneur d’élément et une heure pour afficher le temps actuel du chronomètre dans l’interface utilisateur. Le nom du module peut être une chaîne (il n’est pas nécessaire qu’il s’agisse d’un nom de fichier), mais il doit correspondre au nom utilisé avecJSImportAttribute
(expliqué plus loin dans cet article). La fonctiondom.setInnerText
est importée dans C# et est appelée par la méthode C#SetInnerText
. La méthodeSetInnerText
est présentée plus loin dans cette section.exports.StopwatchSample.Reset()
appelle .NET (StopwatchSample.Reset
) à partir de JS. La méthode C#Reset
redémarre le chronomètre s’il est en cours d’exécution, ou le réinitialise s’il n’est pas en cours d’exécution. La méthodeReset
est présentée plus loin dans cette section.exports.StopwatchSample.Toggle()
appelle .NET (StopwatchSample.Toggle
) à partir de JS. La méthode C#Toggle
démarre ou arrête le chronomètre, selon qu’il est en cours d’exécution ou non. La méthodeToggle
est présentée plus loin dans cette section.runMain()
exécuteProgram.Main
.
setModuleImports
associe un nom à un module de fonctions JS à des fins d’importation dans .NET. Le module JS contient une fonctionwindow.location.href
, qui retourne l’adresse (URL) de la page active. Le nom du module peut être une chaîne (il n’est pas nécessaire qu’il s’agisse d’un nom de fichier), mais il doit correspondre au nom utilisé avecJSImportAttribute
(expliqué plus loin dans cet article). La fonctionwindow.location.href
est importée dans C# et est appelée par la méthode C#GetHRef
. La méthodeGetHRef
est présentée plus loin dans cette section.exports.MyClass.Greeting()
appelle .NET (MyClass.Greeting
) à partir de JS. La méthode C#Greeting
retourne une chaîne qui inclut le résultat de l’appel de la fonctionwindow.location.href
. La méthodeGreeting
est présentée plus loin dans cette section.dotnet.run()
exécuteProgram.Main
.
Module JS :
import { dotnet } from './_framework/dotnet.js'
const { setModuleImports, getAssemblyExports, getConfig, runMain } = await dotnet
.withApplicationArguments("start")
.create();
setModuleImports('main.js', {
dom: {
setInnerText: (selector, time) =>
document.querySelector(selector).innerText = time
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
document.getElementById('reset').addEventListener('click', e => {
exports.StopwatchSample.Reset();
e.preventDefault();
});
const pauseButton = document.getElementById('pause');
pauseButton.addEventListener('click', e => {
const isRunning = exports.StopwatchSample.Toggle();
pauseButton.innerText = isRunning ? 'Pause' : 'Start';
e.preventDefault();
});
await runMain();
import { dotnet } from './_framework/dotnet.js'
const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.withApplicationArgumentsFromQuery()
.create();
setModuleImports('main.js', {
window: {
location: {
href: () => globalThis.window.location.href
}
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting();
console.log(text);
document.getElementById('out').innerHTML = text;
await dotnet.run();
import { dotnet } from './dotnet.js'
const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);
const { setModuleImports, getAssemblyExports, getConfig } =
await dotnet.create();
setModuleImports("main.js", {
window: {
location: {
href: () => globalThis.window.location.href
}
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting();
console.log(text);
document.getElementById("out").innerHTML = text;
await dotnet.run();
Pour importer une fonction JS afin qu’elle puisse être appelée à partir du code C#, utilisez le nouveau JSImportAttribute sur une signature de méthode correspondante. Le premier paramètre de JSImportAttribute est le nom de la fonction JS à importer, et le deuxième paramètre est le nom du module.
Dans l’exemple suivant, la fonction dom.setInnerText
est appelée à partir du module main.js
quand la méthode SetInnerText
est appelée :
[JSImport("dom.setInnerText", "main.js")]
internal static partial void SetInnerText(string selector, string content);
Dans l’exemple suivant, la fonction window.location.href
est appelée à partir du module main.js
quand la méthode GetHRef
est appelée :
[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();
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 en tant que valeurs de retour, sur les méthodes importées et exportées.
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);
Pour exporter une méthode .NET afin qu’elle puisse être appelée à partir de JS, utilisez JSExportAttribute.
Dans l’exemple suivant, chaque méthode est exportée vers JS, et peut être appelée à partir de fonctions JS :
- La méthode
Toggle
démarre ou arrête le chronomètre, en fonction de son état d’exécution. - La méthode
Reset
redémarre le chronomètre s’il est en cours d’exécution, ou le réinitialise s’il n’est pas en cours d’exécution. - La méthode
IsRunning
indique si le chronomètre est en cours d’exécution.
[JSExport]
internal static bool Toggle()
{
if (stopwatch.IsRunning)
{
stopwatch.Stop();
return false;
}
else
{
stopwatch.Start();
return true;
}
}
[JSExport]
internal static void Reset()
{
if (stopwatch.IsRunning)
stopwatch.Restart();
else
stopwatch.Reset();
Render();
}
[JSExport]
internal static bool IsRunning() => stopwatch.IsRunning;
Dans l’exemple suivant, la méthode Greeting
retourne une chaîne qui inclut le résultat de l’appel de la méthode GetHRef
. Comme cela a déjà été indiqué, la méthode C# GetHref
appelle JS pour la fonction window.location.href
à partir du module main.js
. window.location.href
retourne l’adresse (URL) de la page active :
[JSExport]
internal static string Greeting()
{
var text = $"Hello, World! Greetings from {GetHRef()}";
Console.WriteLine(text);
return text;
}
Modèles de charge de travail et de projet expérimentaux
Pour illustrer la fonctionnalité d’interopérabilité JS et obtenir des modèles de projet d’interopérabilité JS, installez la charge de travail wasm-experimental
:
dotnet workload install wasm-experimental
La charge de travail wasm-experimental
contient deux modèles de projet : wasmbrowser
et wasmconsole
. Il s’agit de modèles expérimentaux pour le moment, ce qui signifie que le workflow de développeur associé aux modèles évolue. Toutefois, les API JS et .NET utilisées dans les modèles sont prises en charge dans .NET 8, et fournissent les bases de l’utilisation de .NET sur WASM à partir de JS.
Les modèles peuvent également être installés à partir du package NuGet Microsoft.NET.Runtime.WebAssembly.Templates
avec la commande suivante :
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Application de navigateur
Vous pouvez créer une application de navigateur avec le modèle wasmbrowser
à partir de la ligne de commande. Cela vous permet de créer une application web illustrant l’utilisation simultanée de .NET et de JS dans un navigateur :
dotnet new wasmbrowser
Dans Visual Studio, vous pouvez également créer l’application à l’aide du modèle de projet WebAssembly Browser App.
Générez l’application à partir de Visual Studio ou à l’aide de l’interface .NET CLI :
dotnet build
Générez et exécutez l’application à partir de Visual Studio ou à l’aide de l’interface .NET CLI :
dotnet run
En guise d’alternative, vous pouvez installer et utiliser la commande dotnet serve
:
dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish
Dans l’exemple précédent, l’espace réservé {TARGET FRAMEWORK}
correspond au moniker de framework cible.
Application de console Node.js
Vous pouvez créer une application de console avec le modèle wasmconsole
. Cela vous permet de créer une application qui s’exécute sur WASM en tant qu’application de console Node.js ou V8 :
dotnet new wasmconsole
Dans Visual Studio, vous pouvez également créer l’application à l’aide du modèle de projet WebAssembly Console App.
Générez l’application à partir de Visual Studio ou à l’aide de l’interface .NET CLI :
dotnet build
Générez et exécutez l’application à partir de Visual Studio ou à l’aide de l’interface .NET CLI :
dotnet run
En guise d’alternative, vous pouvez démarrer n’importe quel serveur de fichiers statiques à partir du répertoire de sortie de publication qui contient le fichier main.mjs
:
node bin/$(Configuration)/{TARGET FRAMEWORK}/{PATH}/main.mjs
Dans l’exemple précédent, l’espace réservé {TARGET FRAMEWORK}
est le moniker de framework cible, et l’espace réservé {PATH}
est le chemin d’accès au fichier main.mjs
.
Ressources supplémentaires
- Configuration et hébergement d’applications WebAssembly .NET
- Documentation de l’API
- Interopérabilité JavaScript JSImport/JSExport avec ASP.NET Core Blazor
- Dans le dépôt GitHub
dotnet/runtime
: - Utiliser .NET à partir d’une application JavaScript dans .NET 7