Sdílet prostřednictvím


Spuštění .NET z JavaScriptu

Tento článek vysvětluje, jak spouštět .NET z JavaScriptu (JS) pomocí/JS[JSImport][JSExport] zprostředkovatele komunikace.

Další pokyny najdete v pokynech ke konfiguraci a hostování aplikací .NET WebAssembly v úložišti .NET Runtime (dotnet/runtime) Na GitHubu.

Stávající JS aplikace můžou používat rozšířenou podporu webAssembly na straně klienta k opětovnému použití knihoven .NET z JS nebo k sestavení nového . Aplikace a architektury založené na platformě NET

Poznámka:

Tento článek se zaměřuje na spouštění .NET z JS aplikací bez jakékoli závislosti na Blazor. Pokyny k používání [JSImport][JSExport]/zprostředkovatele komunikace v aplikacích najdete v Blazor WebAssembly tématu JavaScript JSImport/JSExport spolupráce s ASP.NET Core .Blazor

Tyto přístupy jsou vhodné, pokud očekáváte, že se budou spouštět pouze ve službě WebAssembly (WASM). Knihovny můžou provést kontrolu za běhu a určit, jestli je aplikace spuštěná WASM voláním OperatingSystem.IsBrowser.

Požadavky

Nainstalujte nejnovější verzi sady .NET SDK.

wasm-tools Nainstalujte úlohu do příkazového prostředí pro správu, které přináší související cíle NÁSTROJE MSBuild:

dotnet workload install wasm-tools

Nástroje je možné nainstalovat také prostřednictvím instalačního programu sady Visual Studio v rámci úlohy vývoje pro ASP.NET a web v instalačním programu sady Visual Studio. V seznamu volitelných komponent vyberte možnost nástrojů sestavení .NET WebAssembly.

Volitelně můžete nainstalovat wasm-experimental úlohu, která obsahuje experimentální šablony projektů pro zahájení práce s .NET na WebAssembly v aplikaci prohlížeče (WebAssembly Browser App) nebo v Node.js konzolové aplikaci (Webová aplikace konzoly WebAssembly). Tato úloha není nutná, pokud plánujete integrovat JS[JSExport][JSImport]/interoperabilitu do existující JS aplikace.

dotnet workload install wasm-experimental

Šablony lze také nainstalovat z Microsoft.NET.Runtime.WebAssembly.Templates balíčku NuGet pomocí následujícího příkazu:

dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates

Další informace najdete v části Experimentální úlohy a šablony projektů.

Obor názvů

Rozhraní API pro interoperabilitu JS popsané v tomto článku je řízeno atributy v System.Runtime.InteropServices.JavaScript oboru názvů.

Konfigurace projektu

Konfigurace projektu (.csproj) pro povolení JS spolupráce:

  • Nastavte moniker cílové architektury ({TARGET FRAMEWORK}zástupný symbol):

    <TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
    

    Podporuje se .NET 7 (net7.0) nebo novější.

  • AllowUnsafeBlocks Povolte vlastnost, která umožňuje generátoru kódu v kompilátoru Roslyn používat ukazatele pro JS interoperabilitu:

    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    

    Upozorňující

    Rozhraní API pro interoperabilitu JS vyžaduje povolení AllowUnsafeBlocks. Při implementaci vlastního nebezpečného kódu v aplikacích .NET buďte opatrní, což může představovat rizika zabezpečení a stability. Další informace naleznete v tématu Nebezpečný kód, typy ukazatelů a ukazatele funkce.

Následuje příklad souboru projektu (.csproj) po konfiguraci. Zástupný {TARGET FRAMEWORK} symbol je cílová architektura:

<Project Sdk="Microsoft.NET.Sdk.WebAssembly">

  <PropertyGroup>
    <TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

</Project>
  • Nastavte moniker cílové architektury:

    <TargetFramework>net7.0</TargetFramework>
    

    Podporuje se .NET 7 (net7.0) nebo novější.

  • Zadejte browser-wasm identifikátor modulu runtime:

    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
    
  • Zadejte typ výstupu spustitelného souboru:

    <OutputType>Exe</OutputType>
    
  • AllowUnsafeBlocks Povolte vlastnost, která umožňuje generátoru kódu v kompilátoru Roslyn používat ukazatele pro JS interoperabilitu:

    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    

    Upozorňující

    Rozhraní API pro interoperabilitu JS vyžaduje povolení AllowUnsafeBlocks. Při implementaci vlastního nebezpečného kódu v aplikacích .NET buďte opatrní, což může představovat rizika zabezpečení a stability. Další informace naleznete v tématu Nebezpečný kód, typy ukazatelů a ukazatele funkce.

  • Zadejte WasmMainJSPath odkaz na soubor na disku. Tento soubor se publikuje v aplikaci, ale pokud integrujete .NET do existující JS aplikace, použití souboru se nevyžaduje.

    V následujícím příkladu JS je main.jssoubor na disku , ale jakýkoli název JS souboru je možné použít:

    <WasmMainJSPath>main.js</WasmMainJSPath>
    

Ukázkový soubor projektu (.csproj) po konfiguraci:

<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>

Spolupráce JavaScriptu zapnutá WASM

Rozhraní API v následujícím příkladu se naimportují z dotnet.js. Tato rozhraní API umožňují nastavit pojmenované moduly, které se dají importovat do kódu jazyka C# a volat metody vystavené kódem .NET, včetně Program.Main.

Důležité

Import a export v tomto článku jsou definovány z pohledu .NET:

  • Aplikace importuje JS metody, aby je bylo možné volat z .NET.
  • Aplikace exportuje metody .NET, aby je bylo možné volat z JS.

V následujícím příkladu:

  • Tento dotnet.js soubor slouží k vytvoření a spuštění modulu runtime .NET WebAssembly. dotnet.js se generuje jako součást výstupu sestavení aplikace.

    Důležité

    Pokud se chcete integrovat s existující aplikací, zkopírujte obsah výstupní složky publikování† do prostředků nasazení stávající aplikace, aby bylo možné ho obsluhovat společně se zbytkem aplikace. V případě produkčních nasazení publikujte aplikaci příkazem dotnet publish -c Release v příkazovém prostředí a nasaďte obsah výstupní složky s aplikací.

    †Výsložka výstupu je cílové umístění profilu publikování. Výchozí hodnota pro Release profil v .NET 8 nebo novější je bin/Release/{TARGET FRAMEWORK}/publish, kde {TARGET FRAMEWORK} zástupný symbol je cílová architektura (například net8.0).

  • dotnet.create() nastaví modul runtime .NET WebAssembly.

  • setModuleImports přidruží název k modulu JS funkcí pro import do .NET. Modul JS obsahuje dom.setInnerText funkci, která přijímá a selektor prvků a čas k zobrazení aktuálního času stopek v uživatelském rozhraní. Název modulu může být libovolný řetězec (nemusí to být název souboru), ale musí odpovídat názvu použitému v JSImportAttribute tomto článku (vysvětleno dále v tomto článku). Funkce dom.setInnerText je importována do jazyka C# a volána metodou SetInnerTextjazyka C#. Metoda SetInnerText se zobrazí dále v této části.

  • exports.StopwatchSample.Reset() volání do .NET (StopwatchSample.Reset) z JS. Metoda Reset C# restartuje stopky, pokud je spuštěná nebo resetuje, pokud není spuštěná. Metoda Reset se zobrazí dále v této části.

  • exports.StopwatchSample.Toggle() volání do .NET (StopwatchSample.Toggle) z JS. Metoda Toggle C# spustí nebo zastaví stopky v závislosti na tom, jestli je aktuálně spuštěná nebo ne. Metoda Toggle se zobrazí dále v této části.

  • runMain() spouští Program.Main.

  • setModuleImports přidruží název k modulu JS funkcí pro import do .NET. Modul JS obsahuje window.location.href funkci, která vrací adresu aktuální stránky (URL). Název modulu může být libovolný řetězec (nemusí to být název souboru), ale musí odpovídat názvu použitému v JSImportAttribute tomto článku (vysvětleno dále v tomto článku). Funkce window.location.href je importována do jazyka C# a volána metodou GetHRefjazyka C#. Metoda GetHRef se zobrazí dále v této části.

  • exports.MyClass.Greeting() volání do .NET (MyClass.Greeting) z JS. Metoda Greeting C# vrátí řetězec, který obsahuje výsledek volání window.location.href funkce. Metoda Greeting se zobrazí dále v této části.

  • dotnet.run() spouští Program.Main.

JS modul:

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

Pokud chcete naimportovat JS funkci, aby ji bylo možné volat z jazyka C#, použijte novou JSImportAttribute funkci u odpovídajícího podpisu metody. Prvním parametrem funkce JSImportAttribute , která JS se má importovat, a druhým parametrem je název modulu.

V následujícím příkladu dom.setInnerText se funkce volá z main.js modulu, když SetInnerText se volá metoda:

[JSImport("dom.setInnerText", "main.js")]
internal static partial void SetInnerText(string selector, string content);

V následujícím příkladu window.location.href se funkce volá z main.js modulu, když GetHRef se volá metoda:

[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();

V podpisu importované metody můžete použít typy .NET pro parametry a návratové hodnoty, které jsou automaticky zařazovány modulem runtime. Slouží JSMarshalAsAttribute<T> k řízení způsobu, jakým jsou importované parametry metody zařazovány. Můžete se například rozhodnout, že zařadíte jako long System.Runtime.InteropServices.JavaScript.JSType.Number nebo System.Runtime.InteropServices.JavaScript.JSType.BigInt. Zpětná volání můžete předat Action/Func<TResult> jako parametry, které jsou zařazovány jako volatelné JS funkce. Můžete předat odkazy na objekty i JS spravované objekty a jsou zařazovány jako proxy objekty, přičemž objekt je aktivní v rámci hranice, dokud se proxy neshromáždí. Můžete také importovat a exportovat asynchronní metody s Task výsledkem, které jsou zařazovány jako JS přísliby. Většina zařazovaných typů funguje v obou směrech jako parametry a jako návratové hodnoty u importovaných i exportovaných metod.

Následující tabulka uvádí podporované mapování typů.

.NET JavaScript Nullable Task➔k Promise JSMarshalAs volitelný Array of
Boolean Boolean Podporuje se Podporuje se Podporuje se Nepodporováno
Byte Number Podporuje se Podporuje se Podporuje se Podporuje se
Char String Podporuje se Podporuje se Podporuje se Nepodporováno
Int16 Number Podporuje se Podporuje se Podporuje se Nepodporováno
Int32 Number Podporuje se Podporuje se Podporuje se Podporuje se
Int64 Number Podporuje se Podporuje se Nepodporováno Nepodporováno
Int64 BigInt Podporuje se Podporuje se Nepodporováno Nepodporováno
Single Number Podporuje se Podporuje se Podporuje se Nepodporováno
Double Number Podporuje se Podporuje se Podporuje se Podporuje se
IntPtr Number Podporuje se Podporuje se Podporuje se Nepodporováno
DateTime Date Podporuje se Podporuje se Nepodporováno Nepodporováno
DateTimeOffset Date Podporuje se Podporuje se Nepodporováno Nepodporováno
Exception Error Nepodporováno Podporuje se Podporuje se Nepodporováno
JSObject Object Nepodporováno Podporuje se Podporuje se Podporuje se
String String Nepodporováno Podporuje se Podporuje se Podporuje se
Object Any Nepodporováno Podporuje se Nepodporováno Podporuje se
Span<Byte> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Span<Int32> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Span<Double> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
ArraySegment<Byte> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
ArraySegment<Int32> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
ArraySegment<Double> MemoryView Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Task Promise Nepodporováno Nepodporováno Podporuje se Nepodporováno
Action Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Action<T1> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Action<T1, T2> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Action<T1, T2, T3> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Func<TResult> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Func<T1, TResult> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Func<T1, T2, TResult> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno
Func<T1, T2, T3, TResult> Function Nepodporováno Nepodporováno Nepodporováno Nepodporováno

Následující podmínky platí pro mapování typů a zařazované hodnoty:

  • Sloupec Array of označuje, zda lze typ .NET zařaďovat jako JSArray. Příklad: C# int[] (Int32) namapovaný na JSArray s Number.
  • Při předávání JS hodnoty do jazyka C# s hodnotou nesprávného typu vyvolá architektura ve většině případů výjimku. Architektura neprovádí kontrolu JStypu kompilace .
  • JSObject, ExceptionTask a ArraySegment vytvořte GCHandle a proxy. Odstranění můžete aktivovat v kódu vývojáře nebo povolit uvolnění paměti .NET (GC) později. Tyto typy mají značné nároky na výkon.
  • Array: Zařazování pole vytvoří kopii pole v JS rozhraní .NET nebo .NET.
  • MemoryView
    • MemoryViewJS je třída pro modul runtime .NET WebAssembly pro zařazování Span a ArraySegment.
    • Na rozdíl od zařazování pole zařazování nebo ArraySegment nezařazování Span kopie podkladové paměti.
    • MemoryView lze správně vytvořit instanci pouze modulem runtime .NET WebAssembly. Proto není možné importovat funkci jako metodu JS .NET, která má parametr Span nebo ArraySegment.
    • MemoryView vytvoření pro volání Span zprostředkovatele je platné pouze po dobu trvání volání zprostředkovatele komunikace. Jak Span je přiděleno v zásobníku volání, který se nezachovává po volání zprostředkovatele komunikace, není možné exportovat metodu Span.NET, která vrací .
    • MemoryView vytvořeno pro ArraySegment přežití po volání zprostředkovatele komunikace a je užitečné pro sdílení vyrovnávací paměti. Volání dispose() vytvořeného MemoryView pro ArraySegment zlikviduje proxy server a odepnou základní pole .NET. Doporučujeme zavolat dispose() do try-finally bloku pro MemoryView.

Funkce přístupné pro globální obor názvů je možné importovat pomocí globalThis předpony v názvu funkce a pomocí atributu [JSImport] bez zadání názvu modulu. V následujícím příkladu console.log je předpona globalThis. Importovaná funkce je volána metodou jazyka C# Log , která přijímá řetězcovou zprávu jazyka C# (message) a zařadí řetězec jazyka JSString console.logC# na :

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

Chcete-li exportovat metodu .NET, aby ji bylo možné volat z JS, použijte JSExportAttribute.

V následujícím příkladu je každá metoda exportována do JS funkcí a lze ji volat z JS funkcí:

  • Metoda Toggle spustí nebo zastaví stopky v závislosti na jeho spuštěném stavu.
  • Metoda Reset restartuje stopky, pokud je spuštěná nebo resetuje, pokud není spuštěná.
  • Metoda IsRunning označuje, jestli je stopky spuštěné.
[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;

V následujícím příkladu Greeting vrátí metoda řetězec, který obsahuje výsledek volání GetHRef metody. Jak je znázorněno výše, metoda jazyka GetHref C# volá JS funkci window.location.href z main.js modulu. window.location.href vrátí adresu aktuální stránky (URL):

[JSExport]
internal static string Greeting()
{
    var text = $"Hello, World! Greetings from {GetHRef()}";
    Console.WriteLine(text);
    return text;
}

Experimentální úlohy a šablony projektů

Pokud chcete předvést JS funkce vzájemné spolupráce a získat JS šablony projektů spolupráce, nainstalujte wasm-experimental úlohu:

dotnet workload install wasm-experimental

Úloha wasm-experimental obsahuje dvě šablony projektu: wasmbrowser a wasmconsole. Tyto šablony jsou v tuto chvíli experimentální, což znamená, že se vyvíjí pracovní postup vývojáře pro šablony. Rozhraní .NET a JS rozhraní API použitá v šablonách jsou však podporována v .NET 8 a poskytují základ pro použití rozhraní .NET WASM z JS.

Šablony lze také nainstalovat z Microsoft.NET.Runtime.WebAssembly.Templates balíčku NuGet pomocí následujícího příkazu:

dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates

Aplikace prohlížeče

Aplikaci prohlížeče můžete vytvořit pomocí wasmbrowser šablony z příkazového řádku, která vytvoří webovou aplikaci, která demonstruje použití .NET a JS společně v prohlížeči:

dotnet new wasmbrowser

Alternativně v sadě Visual Studio můžete aplikaci vytvořit pomocí WebAssembly Browser App šablony projektu.

Sestavte aplikaci ze sady Visual Studio nebo pomocí rozhraní příkazového řádku .NET:

dotnet build

Sestavte a spusťte aplikaci ze sady Visual Studio nebo pomocí rozhraní příkazového řádku .NET:

dotnet run

Případně nainstalujte a použijte dotnet serve příkaz:

dotnet serve -d:bin/$(Configuration)/{TARGET FRAMEWORK}/publish

V předchozím příkladu {TARGET FRAMEWORK} je zástupný symbol moniker cílové architektury.

Aplikace konzoly Node.js

Pomocí šablony můžete vytvořit konzolovou aplikaci wasmconsole , která vytvoří aplikaci, která běží WASM jako Node.js nebo konzolová aplikace V8 :

dotnet new wasmconsole

Alternativně v sadě Visual Studio můžete aplikaci vytvořit pomocí WebAssembly Console App šablony projektu.

Sestavte aplikaci ze sady Visual Studio nebo pomocí rozhraní příkazového řádku .NET:

dotnet build

Sestavte a spusťte aplikaci ze sady Visual Studio nebo pomocí rozhraní příkazového řádku .NET:

dotnet run

Případně spusťte libovolný statický souborový server z výstupního adresáře publikování, který soubor obsahuje main.mjs :

node bin/$(Configuration)/{TARGET FRAMEWORK}/{PATH}/main.mjs

V předchozím příkladu {TARGET FRAMEWORK} je zástupný symbol moniker cílové architektury a {PATH} zástupný symbol je cesta k main.mjs souboru.

Další materiály