JavaScript [JSImport]
/[JSExport]
interop met een WebAssembly Browser App-project
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
In dit artikel wordt uitgelegd hoe u een WebAssembly Browser App-project instelt om .NET uit te voeren vanuit JavaScript (JS) met behulp van JS[JSImport]
/[JSExport]
interop. Zie JavaScript [JSImport]/[JSExport]' interop in .NET WebAssemblyvoor meer informatie en voorbeelden.
Zie de .NET WebAssembly-toepassingen configureren en hosten richtlijnen in de GitHub-opslagplaats voor .NET Runtime (dotnet/runtime
) voor aanvullende richtlijnen.
Bestaande JS-apps kunnen gebruikmaken van de uitgebreide client-side WebAssembly-ondersteuning om .NET-bibliotheken vanuit JS opnieuw te gebruiken of om nieuwe .NET-gebouwde apps en frameworks te ontwikkelen.
Notitie
Dit artikel is gericht op het uitvoeren van .NET vanuit JS-apps zonder enige afhankelijkheid van Blazor. Voor hulp bij het gebruik van [JSImport]
/[JSExport]
interop in Blazor WebAssembly-apps, zie JavaScript JSImport/JSExport interop met ASP.NET Core Blazor.
Deze benaderingen zijn geschikt wanneer u alleen verwacht dat de Blazor-app wordt uitgevoerd op WebAssembly (WASM). Bibliotheken kunnen een runtimecontrole uitvoeren om te bepalen of de app wordt uitgevoerd op WASM door OperatingSystem.IsBrowseraan te roepen.
Voorwaarden
Installeer de wasm-tools
workload in een beheeropdrachtshell, die de gerelateerde MSBuild-doelen bevat:
dotnet workload install wasm-tools
De hulpprogramma's kunnen ook worden geïnstalleerd via het installatieprogramma van Visual Studio onder de ASP.NET en webontwikkeling workload in het installatieprogramma van Visual Studio. Selecteer in de lijst met optionele onderdelen de optie .NET WebAssembly-buildhulpprogramma's.
Installeer eventueel de wasm-experimental
workload, waarmee de volgende experimentele projectsjablonen worden toegevoegd:
- WebAssembly Browser-app om aan de slag te gaan met .NET op WebAssembly in een browser-app.
- WebAssembly-console-app om aan de slag te gaan in een console-app op basis van Node.js.
Na de installatie van de workload kunnen deze nieuwe sjablonen worden geselecteerd bij het maken van een nieuw project. Deze workload is niet vereist als u van plan bent om JS[JSImport]
/[JSExport]
interoperabiliteit te integreren in een bestaande JS-app.
dotnet workload install wasm-experimental
De sjablonen kunnen ook worden geïnstalleerd vanuit het Microsoft.NET.Runtime.WebAssembly.Templates
NuGet-pakket met de volgende opdracht:
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Zie de sectie Experimentele workload en projectsjablonen voor meer informatie.
Namespace
De JS interop-API die in dit artikel wordt beschreven, wordt beheerd door kenmerken in de System.Runtime.InteropServices.JavaScript naamruimte.
Projectconfiguratie
Een project (.csproj
) configureren om JS interop in te schakelen:
Stel de doelframeworkmoniker in bij (
{TARGET FRAMEWORK}
tijdelijke aanduiding):<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
.NET 7 (
net7.0
) of hoger wordt ondersteund.Schakel de eigenschap AllowUnsafeBlocks in, waarmee de codegenerator in de Roslyn-compiler pointers kan gebruiken voor JS interop:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Waarschuwing
Voor de JS interop-API moet AllowUnsafeBlocksworden ingeschakeld. Wees voorzichtig bij het implementeren van uw eigen onveilige code in .NET-apps, waardoor beveiligings- en stabiliteitsrisico's kunnen optreden. Zie Onveilige code, aanwijzertypen en functiepointersvoor meer informatie.
Hier volgt een voorbeeld van een projectbestand (.csproj
) na de configuratie. De tijdelijke aanduiding {TARGET FRAMEWORK}
is het doelframework:
<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
<PropertyGroup>
<TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Stel de doelframework-moniker in:
<TargetFramework>net7.0</TargetFramework>
.NET 7 (
net7.0
) of hoger wordt ondersteund.Geef
browser-wasm
op voor de runtime-id:<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
Geef een uitvoertype van het uitvoerbare bestand op:
<OutputType>Exe</OutputType>
Schakel de eigenschap AllowUnsafeBlocks in, waarmee de codegenerator in de Roslyn-compiler pointers kan gebruiken voor JS interop:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Waarschuwing
Voor de JS interop-API moet AllowUnsafeBlocksworden ingeschakeld. Wees voorzichtig bij het implementeren van uw eigen onveilige code in .NET-apps, waardoor beveiligings- en stabiliteitsrisico's kunnen optreden. Zie Onveilige code, aanwijzertypen en functiepointersvoor meer informatie.
Geef
WasmMainJSPath
op om naar een bestand op schijf te verwijzen. Dit bestand wordt gepubliceerd met de app, maar het gebruik van het bestand is niet vereist als u .NET in een bestaande JS-app integreert.In het volgende voorbeeld is het JS bestand op schijf
main.js
, maar een JS bestandsnaam is toegestaan:<WasmMainJSPath>main.js</WasmMainJSPath>
Voorbeeldprojectbestand (.csproj
) na configuratie:
<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>
JavaScript-interoperabiliteit in WASM
API's in het volgende voorbeeld worden geïmporteerd uit dotnet.js
. Met deze API's kunt u benoemde modules instellen die kunnen worden geïmporteerd in uw C#-code en methoden aanroepen die worden weergegeven door uw .NET-code, inclusief Program.Main
.
Belangrijk
'Importeren' en 'exporteren' in dit artikel worden gedefinieerd vanuit het perspectief van .NET:
- Een app importeert JS methoden zodat deze kunnen worden aangeroepen vanuit .NET.
- De app exporteert .NET-methoden, zodat ze kunnen worden aangeroepen vanuit JS.
In het volgende voorbeeld:
Het
dotnet.js
-bestand wordt gebruikt om de .NET WebAssembly-runtime te maken en te starten.dotnet.js
wordt gegenereerd als onderdeel van de build-uitvoer van de app.Belangrijk
Als u wilt integreren met een bestaande app, kopieert u de inhoud van de uitvoermap voor publiceren† naar de implementatieassets van de bestaande app, zodat deze samen met de rest van de app kan worden geleverd. Voor productie-implementaties publiceert u de app met de opdracht
dotnet publish -c Release
in een opdrachtshell en implementeert u de inhoud van de uitvoermap met de app.†De uitvoermap Publiceren is de doellocatie van uw publicatieprofiel. De standaardinstelling voor een Release-profiel in .NET 8 of hoger is
bin/Release/{TARGET FRAMEWORK}/publish
, waarbij de tijdelijke aanduiding{TARGET FRAMEWORK}
het doelframework is (bijvoorbeeldnet8.0
).dotnet.create()
stelt de .NET WebAssembly-runtime in.
setModuleImports
een naam koppelt aan een module van JS functies voor importeren in .NET. De JS-module bevat eendom.setInnerText
-functie die een elementselector en een tijd ontvangen om de huidige stopwatchtijd in de gebruikersinterface weer te geven. De naam van de module kan elke tekenreeks zijn (deze hoeft geen bestandsnaam te zijn), maar moet overeenkomen met de naam die wordt gebruikt met deJSImportAttribute
(verderop in dit artikel uitgelegd). Dedom.setInnerText
-functie wordt geïmporteerd in C# en aangeroepen door de C#-methodeSetInnerText
. De methodeSetInnerText
wordt verderop in deze sectie weergegeven.exports.StopwatchSample.Reset()
roept aan in .NET (StopwatchSample.Reset
) vanuit JS. DeReset
C#-methode start de stopwatch opnieuw op als deze wordt uitgevoerd of opnieuw wordt ingesteld als deze niet wordt uitgevoerd. De methodeReset
wordt verderop in deze sectie weergegeven.exports.StopwatchSample.Toggle()
roept .NET aan (StopwatchSample.Toggle
) vanuit JS. DeToggle
C#-methode start of stopt de stopwatch, afhankelijk van of de stopwatch momenteel loopt of niet. De methodeToggle
wordt verderop in deze sectie weergegeven.runMain()
voertProgram.Main
uit.
setModuleImports
een naam koppelt aan een module van JS functies voor importeren in .NET. De JS-module bevat eenwindow.location.href
functie, die het huidige paginaadres (URL) retourneert. De naam van de module kan elke tekenreeks zijn (deze hoeft geen bestandsnaam te zijn), maar moet overeenkomen met de naam die wordt gebruikt met deJSImportAttribute
(verderop in dit artikel uitgelegd). Dewindow.location.href
-functie wordt geïmporteerd in C# en aangeroepen door de C#-methodeGetHRef
. De methodeGetHRef
wordt verderop in deze sectie weergegeven.exports.MyClass.Greeting()
roept .NET (MyClass.Greeting
) aan vanuit JS. De methodeGreeting
C# retourneert een tekenreeks die het resultaat bevat van het aanroepen van dewindow.location.href
-functie. De methodeGreeting
wordt verderop in deze sectie weergegeven.dotnet.run()
wordtProgram.Main
uitgevoerd.
JS module:
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();
Als u een JS-functie wilt importeren zodat deze vanuit C# kan worden aangeroepen, gebruikt u de nieuwe JSImportAttribute op een handtekening voor overeenkomende methoden. De eerste parameter voor de JSImportAttribute is de naam van de JS-functie die moet worden geïmporteerd en de tweede parameter is de naam van de module.
In het volgende voorbeeld wordt de functie dom.setInnerText
aangeroepen vanuit de main.js
-module wanneer SetInnerText
methode wordt aangeroepen:
[JSImport("dom.setInnerText", "main.js")]
internal static partial void SetInnerText(string selector, string content);
In het volgende voorbeeld wordt de functie window.location.href
aangeroepen vanuit de main.js
-module wanneer GetHRef
methode wordt aangeroepen:
[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();
In de signatuur van de geïmporteerde methode kunt u .NET-typen gebruiken voor parameters en retourwaarden, die automatisch worden gemarshald door de runtime. Gebruik JSMarshalAsAttribute<T> om te bepalen hoe de geïmporteerde methodeparameters worden verwerkt. U kunt er bijvoorbeeld voor kiezen om een long
te converteren naar System.Runtime.InteropServices.JavaScript.JSType.Number of System.Runtime.InteropServices.JavaScript.JSType.BigInt. U kunt Action/Func<TResult> callbacks doorgeven als parameters, die worden omgezet als aanroepbare JS functies. U kunt zowel JS als beheerde objectverwijzingen doorgeven, en ze worden verwerkt als proxyobjecten, waardoor het object actief blijft over de procesgrens totdat de proxy wordt opgeruimd door de garbage collector. U kunt asynchrone methoden ook importeren en exporteren met een Task resultaat, dat als JS promisesworden gemarshald. De meeste marshalltypen werken in beide richtingen, als parameters en als retourwaarden, op zowel geïmporteerde als geëxporteerde methoden.
Voor aanvullende informatie en voorbeelden van de toewijzing van typen, zie JavaScript `[JSImport]`/`[JSExport]` interop in .NET WebAssembly.
Functies die toegankelijk zijn voor de globale naamruimte kunnen worden geïmporteerd met behulp van het globalThis
voorvoegsel in de functienaam en met behulp van het kenmerk [JSImport]
zonder een modulenaam op te geven. In het volgende voorbeeld wordt console.log
voorafgegaan door globalThis
. De geïmporteerde functie wordt aangeroepen door de C#-methode Log
, die een C#-tekenreeksbericht (message
) accepteert en de C#-tekenreeks omzet naar een JSString
voor console.log
:
[JSImport("globalThis.console.log")]
internal static partial void Log([JSMarshalAs<JSType.String>] string message);
Als u een .NET-methode wilt exporteren zodat deze kan worden aangeroepen vanuit JS, gebruikt u de JSExportAttribute.
In het volgende voorbeeld wordt elke methode geëxporteerd naar JS en kan deze worden aangeroepen vanuit JS functies:
- De methode
Toggle
start of stopt de stopwatch, afhankelijk van de actieve status. - De methode
Reset
start de stopwatch opnieuw op als deze wordt uitgevoerd of opnieuw wordt ingesteld als deze niet wordt uitgevoerd. - De methode
IsRunning
geeft aan of de stopwatch loopt.
[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;
In het volgende voorbeeld retourneert de methode Greeting
een tekenreeks die het resultaat bevat van het aanroepen van de methode GetHRef
. Zoals eerder wordt weergegeven, roept de methode GetHref
C#JS aan voor de window.location.href
-functie uit de main.js
-module.
window.location.href
retourneert het huidige paginaadres (URL):
[JSExport]
internal static string Greeting()
{
var text = $"Hello, World! Greetings from {GetHRef()}";
Console.WriteLine(text);
return text;
}
Experimentele workload- en projectsjablonen
Installeer de wasm-experimental
workload om de JS interop-functionaliteit te demonstreren en JS interoperabiliteitsprojectsjablonen te verkrijgen:
dotnet workload install wasm-experimental
De workload wasm-experimental
bevat twee projectsjablonen: wasmbrowser
en wasmconsole
. Deze sjablonen zijn momenteel experimenteel, wat betekent dat de werkstroom voor ontwikkelaars voor de sjablonen zich ontwikkelt. De .NET- en JS API's die in de sjablonen worden gebruikt, worden echter ondersteund in .NET 8 en bieden een basis voor het gebruik van .NET op WASM uit JS.
De sjablonen kunnen ook worden geïnstalleerd vanuit het Microsoft.NET.Runtime.WebAssembly.Templates
NuGet-pakket met de volgende opdracht:
dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates
Browserapp
U kunt een browser-app maken met de wasmbrowser
-sjabloon vanaf de opdrachtregel, waarmee een web-app wordt gemaakt die laat zien hoe u .NET gebruikt en JS samen in een browser:
dotnet new wasmbrowser
U kunt ook in Visual Studio de app maken met behulp van de WebAssembly Browser App projectsjabloon.
Bouw de app vanuit Visual Studio of met behulp van de .NET CLI:
dotnet build
Bouw en voer de app uit vanuit Visual Studio of met behulp van de .NET CLI:
dotnet run
U kunt ook de opdracht dotnet serve
installeren en gebruiken:
dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish
In het voorgaande voorbeeld is de tijdelijke aanduiding {TARGET FRAMEWORK}
het doelframework.
Node.js console-applicatie
U kunt een console-app maken met de wasmconsole
-sjabloon, waarmee een app wordt gemaakt die wordt uitgevoerd onder WASM als een Node.js of V8 console-app:
dotnet new wasmconsole
U kunt ook in Visual Studio de app maken met behulp van de WebAssembly Console App projectsjabloon.
Bouw de app vanuit Visual Studio of met behulp van de .NET CLI:
dotnet build
Bouw en voer de app uit vanuit Visual Studio of met behulp van de .NET CLI:
dotnet run
U kunt ook een statische bestandsserver starten vanuit de uitvoermap van publiceren die het main.mjs
-bestand bevat.
node bin/$(Configuration)/{TARGET FRAMEWORK}/{PATH}/main.mjs
In het vorige voorbeeld is de plaatsaanduiding {TARGET FRAMEWORK}
het -doelframeworken is {PATH}
het pad naar het main.mjs
-bestand.
Aanvullende informatiebronnen
- JavaScript [JSImport]/[JSExport]' interop in .NET WebAssembly
- JavaScript JSImport/JSExport-interop met ASP.NET Core Blazor
- API-documentatie
- In de
dotnet/runtime
GitHub-opslagplaats: - .NET gebruiken vanuit een JavaScript-app in .NET 7