Udostępnij za pośrednictwem


Współdziałanie języka JavaScript [JSImport]/[JSExport] z projektem aplikacji webAssembly Browser

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.

W tym artykule wyjaśniono, jak skonfigurować projekt aplikacji przeglądarki WebAssembly w celu uruchamiania platformy .NET z poziomu języka JavaScript (JS) przy użyciu/JS[JSImport][JSExport] międzyoperacji. Aby uzyskać dodatkowe informacje i przykłady, zobacz Międzyoperacja języka JavaScript "[JSImport]"/"[JSExport]" w zestawie webAssembly platformy .NET.

Aby uzyskać dodatkowe wskazówki, zobacz wskazówki dotyczące konfigurowania i hostowania aplikacji webAssembly platformy .NET w repozytorium GitHub Runtime (dotnet/runtime).

Istniejące JS aplikacje mogą używać rozszerzonej obsługi zestawu WebAssembly po stronie klienta w celu ponownego użycia bibliotek platformy .NET z JS lub do kompilowania nowatorskich elementów . Aplikacje i struktury oparte na platformie NET.

Uwaga

Ten artykuł koncentruje się na uruchamianiu platformy .NET z JS poziomu aplikacji bez żadnej zależności od Blazorprogramu . Aby uzyskać wskazówki dotyczące korzystania z [JSImport]/[JSExport] międzyoperacji w Blazor WebAssembly aplikacjach, zobacz JavaScript JSImport/JSExport interop with ASP.NET Core .Blazor

Te podejścia są odpowiednie, gdy oczekujesz Blazor , że aplikacja zostanie uruchomiona tylko w zestawie WebAssembly (WASM). Biblioteki mogą sprawdzić środowisko uruchomieniowe, aby określić, czy aplikacja jest uruchomiona WASM , wywołując polecenie OperatingSystem.IsBrowser.

Wymagania wstępne

Zestaw .NET SDK (najnowsza wersja)

wasm-tools Zainstaluj obciążenie w administracyjnej powłoce poleceń, co powoduje wprowadzenie powiązanych elementów docelowych programu MSBuild:

dotnet workload install wasm-tools

Narzędzia można również zainstalować za pomocą instalatora programu Visual Studio w ramach obciążenia ASP.NET i tworzenia aplikacji internetowych w instalatorze programu Visual Studio. Wybierz opcję Narzędzia kompilacji zestawu WebAssembly platformy .NET z listy składników opcjonalnych.

Opcjonalnie zainstaluj wasm-experimental obciążenie, które dodaje następujące eksperymentalne szablony projektów:

  • Aplikacja przeglądarki WebAssembly na potrzeby rozpoczynania pracy z platformą .NET w zestawie WebAssembly w aplikacji przeglądarki.
  • Aplikacja konsolowa zestawu WebAssembly na potrzeby rozpoczynania pracy w węźle.js- oparta na aplikacji konsolowej.

Po zainstalowaniu obciążenia można wybrać te nowe szablony podczas tworzenia nowego projektu. To obciążenie nie jest wymagane, jeśli planujesz integrację JS[JSExport][JSImport]/międzyoperacji z istniejącą JS aplikacją.

dotnet workload install wasm-experimental

Szablony można również zainstalować z Microsoft.NET.Runtime.WebAssembly.Templates pakietu NuGet za pomocą następującego polecenia:

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

Aby uzyskać więcej informacji, zobacz sekcję Eksperymentalne obciążenie i szablony projektów.

Przestrzeń nazw

Interfejs JS API międzyoperacyjnych opisany w tym artykule jest kontrolowany przez atrybuty w System.Runtime.InteropServices.JavaScript przestrzeni nazw.

Konfiguracja projektu

Aby skonfigurować projekt (.csproj) w celu włączenia JS międzyoperacjności:

  • Ustaw moniker platformy docelowej ({TARGET FRAMEWORK} symbol zastępczy):

    <TargetFramework>{TARGET FRAMEWORK}</TargetFramework>
    

    Obsługiwana jest platforma .NET 7 (net7.0) lub nowsza wersja.

  • AllowUnsafeBlocks Włącz właściwość , która zezwala generatorowi kodu w kompilatorze Roslyn na używanie wskaźników dla JS międzyoperacyjności:

    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    

    Ostrzeżenie

    Interfejs JS API międzyoperajności wymaga włączenia funkcji AllowUnsafeBlocks. Należy zachować ostrożność podczas implementowania własnego niebezpiecznego kodu w aplikacjach platformy .NET, co może powodować zagrożenia bezpieczeństwa i stabilności. Aby uzyskać więcej informacji, zobacz Niebezpieczny kod, typy wskaźników i wskaźniki funkcji.

Poniżej przedstawiono przykładowy plik projektu (.csproj) po konfiguracji. Symbol {TARGET FRAMEWORK} zastępczy to struktura docelowa:

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

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

</Project>
  • Ustaw moniker platformy docelowej:

    <TargetFramework>net7.0</TargetFramework>
    

    Obsługiwana jest platforma .NET 7 (net7.0) lub nowsza wersja.

  • Określ browser-wasm identyfikator środowiska uruchomieniowego:

    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
    
  • Określ typ danych wyjściowych pliku wykonywalnego:

    <OutputType>Exe</OutputType>
    
  • AllowUnsafeBlocks Włącz właściwość , która zezwala generatorowi kodu w kompilatorze Roslyn na używanie wskaźników dla JS międzyoperacyjności:

    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    

    Ostrzeżenie

    Interfejs JS API międzyoperajności wymaga włączenia funkcji AllowUnsafeBlocks. Należy zachować ostrożność podczas implementowania własnego niebezpiecznego kodu w aplikacjach platformy .NET, co może powodować zagrożenia bezpieczeństwa i stabilności. Aby uzyskać więcej informacji, zobacz Niebezpieczny kod, typy wskaźników i wskaźniki funkcji.

  • Określ WasmMainJSPath , aby wskazać plik na dysku. Ten plik jest publikowany w aplikacji, ale użycie pliku nie jest wymagane, jeśli integrujesz platformę .NET z istniejącą JS aplikacją.

    W poniższym przykładzie JS plik na dysku to main.js, ale dowolna JS nazwa pliku jest permissable:

    <WasmMainJSPath>main.js</WasmMainJSPath>
    

Przykładowy plik projektu (.csproj) po konfiguracji:

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

Interopcja języka JavaScript w systemie WASM

Interfejsy API w poniższym przykładzie są importowane z dotnet.jsusługi . Te interfejsy API umożliwiają skonfigurowanie nazwanych modułów, które można zaimportować do kodu języka C# i wywołać do metod uwidocznionych przez kod platformy .NET, w tym Program.Main.

Ważne

"Import" i "export" w tym artykule są definiowane z perspektywy platformy .NET:

  • Aplikacja importuje JS metody, aby można je było wywołać z platformy .NET.
  • Aplikacja eksportuje metody .NET, aby można je było wywołać z JS.

W poniższym przykładzie:

  • Plik dotnet.js jest używany do tworzenia i uruchamiania środowiska uruchomieniowego zestawu WebAssembly platformy .NET. dotnet.js jest generowany jako część danych wyjściowych kompilacji aplikacji.

    Ważne

    Aby zintegrować z istniejącą aplikacją, skopiuj zawartość folderu wyjściowego publikowania† do zasobów wdrożenia istniejącej aplikacji, aby można było ją obsłużyć wraz z aplikacją rest . W przypadku wdrożeń produkcyjnych opublikuj aplikację za pomocą dotnet publish -c Release polecenia w powłoce poleceń i wdróż zawartość folderu wyjściowego za pomocą aplikacji.

    † Folder wyjściowy publikowania jest lokalizacją docelową profilu publikowania. Domyślnym ustawieniem Release profilu na platformie .NET 8 lub nowszym jest bin/Release/{TARGET FRAMEWORK}/publish, gdzie {TARGET FRAMEWORK} symbol zastępczy to platforma docelowa (na przykład net8.0).

  • dotnet.create() Konfiguruje środowisko uruchomieniowe zestawu webAssembly platformy .NET.

  • setModuleImports kojarzy nazwę z modułem JS funkcji do importowania do platformy .NET. Moduł JS zawiera dom.setInnerText funkcję, która akceptuje selektor elementów i czas wyświetlania bieżącego czasu stopera w interfejsie użytkownika. Nazwa modułu może być dowolnym ciągiem (nie musi być nazwą pliku), ale musi być zgodna z nazwą używaną z wartością JSImportAttribute (wyjaśnionej w dalszej części tego artykułu). Funkcja dom.setInnerText jest importowana do języka C# i wywoływana przez metodę SetInnerTextjęzyka C#. Metoda jest wyświetlana SetInnerText w dalszej części tej sekcji.

  • exports.StopwatchSample.Reset() wywołania do platformy .NET (StopwatchSample.Reset) z JS. Reset Metoda języka C# uruchamia stoper, jeśli jest uruchomiony lub resetuje go, jeśli nie jest uruchomiony. Metoda jest wyświetlana Reset w dalszej części tej sekcji.

  • exports.StopwatchSample.Toggle() wywołania do platformy .NET (StopwatchSample.Toggle) z JS. Metoda Toggle języka C# uruchamia lub zatrzymuje stoper w zależności od tego, czy jest aktualnie uruchomiony, czy nie. Metoda jest wyświetlana Toggle w dalszej części tej sekcji.

  • runMain() uruchamia polecenie Program.Main.

  • setModuleImports kojarzy nazwę z modułem JS funkcji do importowania do platformy .NET. Moduł JS zawiera window.location.href funkcję, która zwraca bieżący adres strony (adres URL). Nazwa modułu może być dowolnym ciągiem (nie musi być nazwą pliku), ale musi być zgodna z nazwą używaną z wartością JSImportAttribute (wyjaśnionej w dalszej części tego artykułu). Funkcja window.location.href jest importowana do języka C# i wywoływana przez metodę GetHRefjęzyka C#. Metoda jest wyświetlana GetHRef w dalszej części tej sekcji.

  • exports.MyClass.Greeting() wywołania do platformy .NET (MyClass.Greeting) z JS. Metoda Greeting języka C# zwraca ciąg, który zawiera wynik wywołania window.location.href funkcji. Metoda jest wyświetlana Greeting w dalszej części tej sekcji.

  • dotnet.run() uruchamia polecenie Program.Main.

JS moduł:

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

Aby zaimportować JS funkcję, aby można ją było wywołać z języka C#, należy użyć jej w JSImportAttribute podpisie zgodnej metody. Pierwszy parametr JSImportAttribute to nazwa JS funkcji do zaimportowania, a drugi parametr to nazwa modułu.

W poniższym przykładzie dom.setInnerText funkcja jest wywoływana z modułu main.js , gdy SetInnerText wywoływana jest metoda:

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

W poniższym przykładzie window.location.href funkcja jest wywoływana z modułu main.js , gdy GetHRef wywoływana jest metoda:

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

W zaimportowanym podpisie metody można użyć typów platformy .NET dla parametrów i wartości zwracanych, które są automatycznie obsługiwane przez środowisko uruchomieniowe. Służy JSMarshalAsAttribute<T> do kontrolowania sposobu stosowania importowanych parametrów metody. Na przykład możesz wybrać przeprowadzanie marshalingu long jako lub System.Runtime.InteropServices.JavaScript.JSType.BigIntSystem.Runtime.InteropServices.JavaScript.JSType.Number . Wywołania zwrotne można przekazać Action/Func<TResult> jako parametry, które są wywoływane jako funkcje możliwe do wywołania.JS Można przekazać odwołania do JS obiektów zarządzanych i i są one marshalowane jako obiekty proxy, utrzymując obiekt aktywny przez granicę do momentu, aż serwer proxy zostanie odśmiecany. Możesz również importować i eksportować metody asynchroniczne z Task wynikiem, które są marshalowane jako JS obietnice. Większość typów marshalled działa w obu kierunkach, jako parametry i jako wartości zwracane, zarówno w metodach importowanych, jak i eksportowanych.

Aby uzyskać dodatkowe informacje o mapowaniu typów i przykłady, zobacz Interop języka JavaScript "[JSImport]"/"[JSExport]" w zestawie webAssembly platformy .NET.

Funkcje dostępne w globalnej przestrzeni nazw można importować przy użyciu prefiksu globalThis w nazwie funkcji i przy użyciu atrybutu bez podawania nazwy modułu [JSImport] . W poniższym przykładzie console.log element ma prefiks globalThis. Zaimportowana funkcja jest wywoływana przez metodę języka C#, która akceptuje komunikat ciągu języka C# Log (message) i marshalls ciąg języka C# dla JSString elementu :console.log

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

Aby wyeksportować metodę .NET, aby można ją było wywołać z JSklasy , użyj polecenia JSExportAttribute.

W poniższym przykładzie każda metoda jest eksportowana do JS funkcji i może być wywoływana z JS funkcji:

  • Metoda Toggle uruchamia lub zatrzymuje stoper w zależności od jego stanu działania.
  • Metoda Reset ponownie uruchamia stoper, jeśli jest uruchomiony lub resetuje go, jeśli nie jest uruchomiony.
  • Metoda IsRunning wskazuje, czy stoper jest uruchomiony.
[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;

W poniższym przykładzie Greeting metoda zwraca ciąg zawierający wynik wywołania GetHRef metody . Jak pokazano wcześniej, GetHref metoda języka C# wywołuje JS funkcję window.location.href z modułu main.js . window.location.href Zwraca bieżący adres strony (adres URL):

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

Eksperymentalne szablony obciążeń i projektów

Aby zademonstrować JS funkcjonalność międzyoperajności i uzyskać JS szablony projektów międzyoperajowych, zainstaluj wasm-experimental obciążenie:

dotnet workload install wasm-experimental

Obciążenie wasm-experimental zawiera dwa szablony projektów: wasmbrowser i wasmconsole. Te szablony są obecnie eksperymentalne, co oznacza, że przepływ pracy dewelopera dla szablonów ewoluuje. Jednak platformy .NET i JS interfejsy API używane w szablonach są obsługiwane na platformie .NET 8 i stanowią podstawę do korzystania z platformy .NET na WASM platformie JS.

Szablony można również zainstalować z Microsoft.NET.Runtime.WebAssembly.Templates pakietu NuGet za pomocą następującego polecenia:

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

Aplikacja przeglądarki

Aplikację przeglądarki można utworzyć przy użyciu szablonu wasmbrowser z poziomu wiersza polecenia, która tworzy aplikację internetową, która demonstruje korzystanie z platformy .NET i JS razem w przeglądarce:

dotnet new wasmbrowser

Alternatywnie w programie Visual Studio możesz utworzyć aplikację przy użyciu szablonu WebAssembly Browser App projektu.

Skompiluj aplikację z programu Visual Studio lub przy użyciu interfejsu wiersza polecenia platformy .NET:

dotnet build

Skompiluj i uruchom aplikację z poziomu programu Visual Studio lub przy użyciu interfejsu wiersza polecenia platformy .NET:

dotnet run

Alternatywnie zainstaluj polecenie i użyj polecenia dotnet serve:

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

W poprzednim przykładzie {TARGET FRAMEWORK} symbol zastępczy jest pseudonimem platformy docelowej.

Węzeł.js aplikacja konsolowa

Aplikację konsolową można utworzyć przy użyciu szablonuwasmconsole, który tworzy aplikację działającą w obszarze WASM jako węzełjs lub aplikację konsolową w wersji 8:

dotnet new wasmconsole

Alternatywnie w programie Visual Studio możesz utworzyć aplikację przy użyciu szablonu WebAssembly Console App projektu.

Skompiluj aplikację z programu Visual Studio lub przy użyciu interfejsu wiersza polecenia platformy .NET:

dotnet build

Skompiluj i uruchom aplikację z poziomu programu Visual Studio lub przy użyciu interfejsu wiersza polecenia platformy .NET:

dotnet run

Alternatywnie uruchom dowolny statyczny serwer plików z katalogu wyjściowego publikowania, który zawiera main.mjs plik:

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

W poprzednim przykładzie {TARGET FRAMEWORK} symbol zastępczy jest pseudonimem platformy docelowej, a {PATH} symbol zastępczy jest ścieżką main.mjs do pliku.

Dodatkowe zasoby