JavaScript-Interoperabilität durch [JSImport]/[JSExport] mit ASP.NET Core Blazor

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird erläutert, wie Sie mit der Interop-API [JSImport]/[JSExport] von JavaScript (JS), die für Apps mit .NET 7 oder höher veröffentlicht wurde, mit JavaScript (JS) in clientseitigen Komponenten interagieren.

Blazor bietet seinen eigenen JS-Interoperabilitätsmechanismus, der auf der IJSRuntime-Schnittstelle basiert. Die von Blazor zur Verfügung gestellte JS-Interoperabilität wird in allen Blazor-Rendermodi und für alle Blazor Hybrid-Apps einheitlich unterstützt. IJSRuntime ermöglicht Bibliotheksautoren außerdem das Erstellen von JS-Interoperabilitätsbibliotheken für die Freigabe im Blazor-Ökosystem und bleibt der empfohlene Ansatz für JS-Interoperabilität in Blazor. Weitere Informationen finden Sie in folgenden Artikeln:

In diesem Artikel wird ein alternativer JS-Interop-Ansatz beschrieben, der speziell für clientseitige Komponenten gilt, die in WebAssembly ausgeführt werden. Diese Ansätze eignen sich, wenn die Ausführung ausschließlich in clientseitiger WebAssembly erfolgt. Autor*innen von Bibliotheken können diese Ansätze verwenden, um die JS-Interoperabilität zu optimieren, indem sie zur Laufzeit überprüfen, ob die App in einem Browser (OperatingSystem.IsBrowser) in WebAssembly ausgeführt wird. Die in diesem Artikel beschriebenen Ansätze sollten verwendet werden, um die veraltete JS-Interop-API ohne Marshalling beim Migrieren zu .NET 7 oder höher zu ersetzen.

Hinweis

Dieser Artikel konzentriert sich auf JS-Interop in clientseitigen Komponenten. Anleitungen zum Aufrufen von .NET in JavaScript-Apps finden Sie unter Ausführen von .NET aus JavaScript.

Veraltete JavaScript-Interop-API

Die JS-Interop-API mit IJSUnmarshalledRuntime ist in ASP.NET Core in .NET 7 oder höher veraltet. Folgen Sie den Anweisungen in diesem Artikel, um die veraltete API zu ersetzen.

Voraussetzungen

Laden Sie .NET 7 oder eine höhere Version herunter, und installieren Sie die Software, wenn sie noch nicht auf dem System oder wenn auf dem System nicht die neueste Version installiert ist.

Namespace

Die in diesem Artikel beschriebene JS-Interop-API wird durch Attribute im Namespace System.Runtime.InteropServices.JavaScript gesteuert.

Aktivieren unsicherer Blöcke

Aktivieren Sie die Eigenschaft AllowUnsafeBlocks in der Projektdatei der App, damit der Codegenerator im Roslyn-Compiler Zeiger für die JS-Interoperabilität verwenden kann:

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

Warnung

Die JS-Interop-API erfordert die Aktivierung von AllowUnsafeBlocks. Seien Sie vorsichtig, wenn Sie Ihren eigenen unsicheren Code in .NET-Anwendungen implementieren, da dies zu Sicherheits- und Stabilitätsrisiken führen kann. Weitere Informationen finden Sie unter Unsicherer Code, Zeigertypen und Funktionszeiger.

Aufrufen von JavaScript über .NET

In diesem Abschnitt wird erläutert, wie JS-Funktionen über .NET aufgerufen werden.

In der folgenden CallJavaScript1-Komponente:

  • Das CallJavaScript1-Modul wird mit JSHost.ImportAsync asynchron aus der verbundenenJS-Datei importiert.
  • Die importierte getMessageJS-Funktion wird von GetWelcomeMessage aufgerufen.
  • Die zurückgegebene Zeichenfolge mit der Willkommensnachricht wird über das message-Feld in der Benutzeroberfläche angezeigt.

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

Hinweis

Schließen Sie in Code eine bedingte Überprüfung mit OperatingSystem.IsBrowser ein, um sicherzustellen, dass die JS-Interoperabilität nur von einer Komponente aufgerufen wird, die im Client gerendert wird. Das ist wichtig für Bibliotheken/NuGet-Pakete, die auf serverseitige Komponenten ausgerichtet sind, da sie den Code, der von dieser JS-Interop-API bereitgestellt wird, nicht ausführen können.

Wenn Sie eine JS-Funktion importieren möchten, um sie aus C# aufzurufen, verwenden Sie das [JSImport]-Attribut für eine C#-Methodensignatur, die der Signatur der JS-Funktion entspricht. Der erste Parameter des [JSImport]-Attributs ist der Name der zu importierenden JS-Funktion, und der zweite Parameter ist der Name des JS-Moduls.

Im folgenden Beispiel handelt es sich bei getMessage um eine JS-Funktion, die einen Wert vom Typ string für ein Modul namens CallJavaScript1 zurückgibt. Die C#-Methodensignatur stimmt damit überein: Es werden keine Parameter an die JS-Funktion übergeben, und die JS-Funktion gibt einen Wert vom Typ string zurück. Die JS-Funktion wird von GetWelcomeMessage im C#-Code aufgerufen.

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

Der Namespace der App für die vorstehende Teilklasse CallJavaScript1 lautet BlazorSample. Der Namespace der Komponente heißt BlazorSample.Components.Pages. Wenn Sie die vorstehende Komponente in einer lokalen Test-App verwenden, aktualisieren Sie den Namespace, sodass er der App entspricht. Beispielsweise heißt der Namespace ContosoApp.Components.Pages, wenn der Namespace der App ContosoApp lautet. Weitere Informationen finden Sie unter Razor-Komponenten in ASP.NET Core.

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

Der Namespace der App für die vorstehende Teilklasse CallJavaScript1 lautet BlazorSample. Der Namespace der Komponente heißt BlazorSample.Pages. Wenn Sie die vorstehende Komponente in einer lokalen Test-App verwenden, aktualisieren Sie den Namespace, sodass er der App entspricht. Beispielsweise heißt der Namespace ContosoApp.Pages, wenn der Namespace der App ContosoApp lautet. Weitere Informationen finden Sie unter Razor-Komponenten in ASP.NET Core.

In der importierten Methodensignatur können Sie .NET-Typen für Parameter und Rückgabewerte verwenden, die automatisch von der Laufzeit gemarshallt werden. Sie können mit JSMarshalAsAttribute<T> steuern, wie die importierten Methodenparameter gemarshallt werden. Beispielsweise können Sie entscheiden, ob ein Wert vom Typ long als System.Runtime.InteropServices.JavaScript.JSType.Number oder System.Runtime.InteropServices.JavaScript.JSType.BigInt gemarshallt wird. Sie können Action/Func<TResult>-Rückrufe als Parameter übergeben, die als aufrufbare JS-Funktionen gemarshallt werden. Sie können sowohl JS als auch verwaltete Objektverweise übergeben. Diese werden als Proxyobjekte gemarshallt, wobei das Objekt über die Grenze hinweg aktiv bleibt, bis beim Proxy eine Garbage Collection durchgeführt wird. Zudem können asynchrone Methoden mit einem Task-Ergebnis importiert und exportiert werden. Die Ergebnisse werden als JSZusage gemarshallt. Die meisten gemarshallten Typen funktionieren sowohl bei importierten als auch bei exportierten Methoden in beiden Richtungen, als Parameter und als Rückgabewerte, die im Abschnitt Aufrufen von .NET über JavaScript weiter unten in diesem Artikel behandelt werden.

In der folgenden Tabelle werden die unterstützten Typzuordnungen aufgeführt.

.NET JavaScript Nullable Task zu Promise JSMarshalAs optional Array of
Boolean Boolean Unterstützt Unterstützt Unterstützt Nicht unterstützt
Byte Number Unterstützt Unterstützt Unterstützt Unterstützt
Char String Unterstützt Unterstützt Unterstützt Nicht unterstützt
Int16 Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
Int32 Number Unterstützt Unterstützt Unterstützt Unterstützt
Int64 Number Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Int64 BigInt Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Single Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
Double Number Unterstützt Unterstützt Unterstützt Unterstützt
IntPtr Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
DateTime Date Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
DateTimeOffset Date Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Exception Error Nicht unterstützt Unterstützt Unterstützt Nicht unterstützt
JSObject Object Nicht unterstützt Unterstützt Unterstützt Unterstützt
String String Nicht unterstützt Unterstützt Unterstützt Unterstützt
Object Any Nicht unterstützt Unterstützt Nicht unterstützt Unterstützt
Span<Byte> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Span<Int32> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Span<Double> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Byte> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Int32> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Double> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Task Promise Nicht unterstützt Nicht unterstützt Unterstützt Nicht unterstützt
Action Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1, T2> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1, T2, T3> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, T2, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, T2, T3, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt

Die folgenden Bedingungen gelten für Typzuordnungen und gemarshallte Werte:

  • Die Spalte Array of gibt an, ob der .NET-Typ als JSArray gemarshallt werden kann. Beispiel: C# int[] (Int32) zugeordnet zu JSArray von Number.
  • Wenn ein JS-Wert vom falschen Typ an C# übergeben wird, löst das Framework in den meisten Fällen eine Ausnahme aus. Das Framework führt zur Kompilierzeit keine Typüberprüfung in JS durch.
  • JSObject, Exception, Task und ArraySegment erstellen GCHandle und einen Proxy. Sie können die Entsorgung im Entwicklercode auslösen oder es der .NET-Garbage Collection (GC) überlassen, die Objekte später zu entfernen. Diese Typen bedeuten einen erheblichen Leistungsaufwand.
  • Array: Beim Marshallen eines Arrays wird eine Kopie des Arrays in JS oder .NET erstellt.
  • MemoryView
    • MemoryView ist eine JS-Klasse für die .NET-WebAssembly-Runtime zum Marshallen von Span und ArraySegment.
    • Im Gegensatz zum Marshallen eines Arrays wird beim Marshallen einer Span oder eines ArraySegment keine Kopie des zugrunde liegenden Speichers erstellt.
    • MemoryView kann nur von der .NET-WebAssembly-Runtime ordnungsgemäß instanziiert werden. Daher ist es nicht möglich, eine JS-Funktion als .NET-Methode zu importieren, die über den Parameter Span oder ArraySegment verfügt.
    • Wenn MemoryView für einen Span erstellt wird, gilt sie nur für die Dauer des Interoperabilitätsaufrufs. Da Span der Aufrufliste zugewiesen wird, die nach dem Interoperabilitätsaufruf nicht beibehalten wird, ist es nicht möglich, eine .NET-Methode zu exportieren, die einen Span zurückgibt.
    • MemoryView wird für ein ArraySegment erstellt und auch nach dem Interoperabilitätsaufruf beibehalten und ist damit für die gemeinsame Verwendung eines Puffers nützlich. Durch Aufrufen von dispose() in einer MemoryView, die für ein ArraySegment erstellt wurde, wird der Proxy verworfen und das zugrunde liegende .NET-Array getrennt. Es wird empfohlen, dispose() in einem try-finally-Block für MemoryView aufzurufen.

In der folgenden Tabelle werden die unterstützten Typzuordnungen aufgeführt.

.NET JavaScript Nullable Task zu Promise JSMarshalAs optional Array of
Boolean Boolean Unterstützt Unterstützt Unterstützt Nicht unterstützt
Byte Number Unterstützt Unterstützt Unterstützt Unterstützt
Char String Unterstützt Unterstützt Unterstützt Nicht unterstützt
Int16 Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
Int32 Number Unterstützt Unterstützt Unterstützt Unterstützt
Int64 Number Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Int64 BigInt Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Single Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
Double Number Unterstützt Unterstützt Unterstützt Unterstützt
IntPtr Number Unterstützt Unterstützt Unterstützt Nicht unterstützt
DateTime Date Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
DateTimeOffset Date Unterstützt Unterstützt Nicht unterstützt Nicht unterstützt
Exception Error Nicht unterstützt Unterstützt Unterstützt Nicht unterstützt
JSObject Object Nicht unterstützt Unterstützt Unterstützt Unterstützt
String String Nicht unterstützt Unterstützt Unterstützt Unterstützt
Object Any Nicht unterstützt Unterstützt Nicht unterstützt Unterstützt
Span<Byte> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Span<Int32> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Span<Double> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Byte> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Int32> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
ArraySegment<Double> MemoryView Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Task Promise Nicht unterstützt Nicht unterstützt Unterstützt Nicht unterstützt
Action Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1, T2> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Action<T1, T2, T3> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, T2, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt
Func<T1, T2, T3, TResult> Function Nicht unterstützt Nicht unterstützt Nicht unterstützt Nicht unterstützt

Die folgenden Bedingungen gelten für Typzuordnungen und gemarshallte Werte:

  • Die Spalte Array of gibt an, ob der .NET-Typ als JSArray gemarshallt werden kann. Beispiel: C# int[] (Int32) zugeordnet zu JSArray von Number.
  • Wenn ein JS-Wert vom falschen Typ an C# übergeben wird, löst das Framework in den meisten Fällen eine Ausnahme aus. Das Framework führt zur Kompilierzeit keine Typüberprüfung in JS durch.
  • JSObject, Exception, Task und ArraySegment erstellen GCHandle und einen Proxy. Sie können die Entsorgung im Entwicklercode auslösen oder es der .NET-Garbage Collection (GC) überlassen, die Objekte später zu entfernen. Diese Typen bedeuten einen erheblichen Leistungsaufwand.
  • Array: Beim Marshallen eines Arrays wird eine Kopie des Arrays in JS oder .NET erstellt.
  • MemoryView
    • MemoryView ist eine JS-Klasse für die .NET-WebAssembly-Runtime zum Marshallen von Span und ArraySegment.
    • Im Gegensatz zum Marshallen eines Arrays wird beim Marshallen einer Span oder eines ArraySegment keine Kopie des zugrunde liegenden Speichers erstellt.
    • MemoryView kann nur von der .NET-WebAssembly-Runtime ordnungsgemäß instanziiert werden. Daher ist es nicht möglich, eine JS-Funktion als .NET-Methode zu importieren, die über den Parameter Span oder ArraySegment verfügt.
    • Wenn MemoryView für einen Span erstellt wird, gilt sie nur für die Dauer des Interoperabilitätsaufrufs. Da Span der Aufrufliste zugewiesen wird, die nach dem Interoperabilitätsaufruf nicht beibehalten wird, ist es nicht möglich, eine .NET-Methode zu exportieren, die einen Span zurückgibt.
    • MemoryView wird für ein ArraySegment erstellt und auch nach dem Interoperabilitätsaufruf beibehalten und ist damit für die gemeinsame Verwendung eines Puffers nützlich. Durch Aufrufen von dispose() in einer MemoryView, die für ein ArraySegment erstellt wurde, wird der Proxy verworfen und das zugrunde liegende .NET-Array getrennt. Es wird empfohlen, dispose() in einem try-finally-Block für MemoryView aufzurufen.

Der Modulname im [JSImport]-Attribut und der Aufruf zum Laden des Moduls in der Komponente mit JSHost.ImportAsync müssen übereinstimmen und in der App eindeutig sein. Beim Erstellen einer Bibliothek für die Bereitstellung in einem NuGet-Paket empfehlen wir, den Namespace des NuGet-Pakets als Präfix in den Modulnamen zu verwenden. Im folgenden Beispiel spiegelt der Modulname das Contoso.InteropServices.JavaScript-Paket und einen Ordner der Interop-Klassen für Benutzernachrichten wider (UserMessages):

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

Funktionen, auf die im globalen Namespace zugegriffen werden kann, können mithilfe des Präfixes globalThis im Funktionsnamen und mithilfe des Attributs [JSImport] ohne Angabe eines Modulnamens importiert werden. Im folgenden Beispiel hat console.log das Präfix globalThis. Die importierte Funktion wird von der C#-Log Methode aufgerufen, die eine C#-Zeichenfolgenmeldung (message) akzeptiert und die C#-Zeichenfolge in einen JSString für console.log marshallt:

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

Exportieren Sie Skripts aus einem standardmäßigen JavaScript ES6-Modul, das entweder mit einer Komponente verbunden ist oder mit anderen statischen JavaScript-Ressourcen in einer JS-Datei platziert wird (z. B. wwwroot/js/{FILE NAME}.js, wobei statische JS-Ressourcen in einem Ordner namens js im Ordner wwwroot der App verwaltet werden und der Platzhalter {FILE NAME} für den Dateinamen steht).

Im folgenden Beispiel wird eine JS-Funktion namens getMessage aus einer verbundenen JS-Datei exportiert, die die Willkommensnachricht „Hello from Blazor!“ in Portugiesisch zurückgibt:

CallJavaScript1.razor.js:

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

Aufrufen von .NET über JavaScript

In diesem Abschnitt wird erläutert, wie Sie .NET-Methoden über JSaufrufen.

Die folgende CallDotNet1-Komponente ruft JS auf, das direkt mit dem DOM interagiert, um die Zeichenfolge mit der Willkommensnachricht zu rendern:

  • DasCallDotNetJS-Modul wird asynchron aus der verbundenen JS-Datei für diese Komponente importiert.
  • Die importierte setMessageJS-Funktion wird von SetWelcomeMessage aufgerufen.
  • Die zurückgegebene Willkommensnachricht wird von setMessage über das message-Feld in der Benutzeroberfläche angezeigt.

Wichtig

Im Beispiel dieses Abschnitts wird die JS-Interoperabilität verwendet, um ein DOM-Element rein zu Demonstrationszwecken zu mutieren, nachdem die Komponente in OnAfterRender gerendert wurde. Normalerweise sollten Sie das DOM mit JS nur mutieren, wenn das Objekt nicht mit Blazor interagiert. Der in diesem Abschnitt gezeigte Ansatz ähnelt Fällen, in denen eine JS-Bibliothek von einem Drittanbieter in einer Razor-Komponente verwendet wird, wobei die Komponente über JS-Interoperabilität mit der JS-Bibliothek interagiert, die JS-Bibliothek des Drittanbieters mit einem Teil des DOM interagiert und Blazor nicht direkt an den DOM-Updates für diesen Teil des DOM beteiligt ist. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität).

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

Verwenden Sie das [JSExport]-Attribut, um eine .NET-Methode zu exportieren, damit sie über JS aufgerufen werden kann.

Im folgenden Beispiel:

  • SetWelcomeMessage ruft eine JS-Funktion mit dem Namen setMessage auf. Die JS-Funktion ruft .NET auf, um die Willkommensnachricht von GetMessageFromDotnet zu empfangen, und zeigt die Nachricht in der Benutzeroberfläche an.
  • GetMessageFromDotnet ist eine .NET-Methode mit dem [JSExport]-Attribut, das die Willkommensnachricht „Hello from Blazor!“ in Portugiesisch zurückgibt.

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

Der Namespace der App für die vorstehende Teilklasse CallDotNet1 lautet BlazorSample. Der Namespace der Komponente heißt BlazorSample.Components.Pages. Wenn Sie die vorstehende Komponente in einer lokalen Test-App verwenden, aktualisieren Sie deren Namespace, sodass er der App entspricht. Beispielsweise heißt der Namespace der Komponente ContosoApp.Components.Pages, wenn der Namespace der App ContosoApp lautet. Weitere Informationen finden Sie unter Razor-Komponenten in ASP.NET Core.

Im folgenden Beispiel wird eine JS-Funktion namens setMessage aus einer verbundenen JS-Datei importiert.

Die setMessage-Methode:

  • Ruft globalThis.getDotnetRuntime(0) auf, um die WebAssembly .NET-Laufzeitinstanz für das Aufrufen exportierter .NET-Methoden verfügbar zu machen.
  • Ruft die JS-Exporte der App-Assembly ab. Der Name der App-Assembly lautet im folgenden Beispiel BlazorSample.
  • Ruft die BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet-Methode aus den Exporten (exports) auf. Der zurückgegebene Wert, der die Willkommensnachricht enthält, wird dem <span>-Text der CallDotNet1-Komponente zugewiesen. Der Namespace der App lautet BlazorSample, und der Namespace der CallDotNet1-Komponente heißt 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!";
    }
}

Der Namespace der App für die vorstehende Teilklasse CallDotNet1 lautet BlazorSample. Der Namespace der Komponente heißt BlazorSample.Pages. Wenn Sie die vorstehende Komponente in einer lokalen Test-App verwenden, aktualisieren Sie deren Namespace, sodass er der App entspricht. Beispielsweise heißt der Namespace der Komponente ContosoApp.Pages, wenn der Namespace der App ContosoApp lautet. Weitere Informationen finden Sie unter Razor-Komponenten in ASP.NET Core.

Im folgenden Beispiel wird eine JS-Funktion namens setMessage aus einer verbundenen JS-Datei importiert.

Die setMessage-Methode:

  • Ruft globalThis.getDotnetRuntime(0) auf, um die WebAssembly .NET-Laufzeitinstanz für das Aufrufen exportierter .NET-Methoden verfügbar zu machen.
  • Ruft die JS-Exporte der App-Assembly ab. Der Name der App-Assembly lautet im folgenden Beispiel BlazorSample.
  • Ruft die BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet-Methode aus den Exporten (exports) auf. Der zurückgegebene Wert, der die Willkommensnachricht enthält, wird dem <span>-Text der CallDotNet1-Komponente zugewiesen. Der Namespace der App lautet BlazorSample, und der Namespace der CallDotNet1-Komponente heißt 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();
}

Hinweis

Aufrufe von getAssemblyExports zum Abrufen der Exporte können in einem JavaScript-Initialisierer erfolgen, damit sie in der gesamten App verfügbar sind.

Mehrere Modulimportaufrufe

Nachdem ein JS-Modul geladen wurde, sind die JS-Funktionen des Moduls für die Komponenten und Klassen der App verfügbar, solange die App im Browserfenster oder auf einer Registerkarte ausgeführt wird, ohne dass der Benutzer die App manuell neu lädt. JSHost.ImportAsync kann ohne erhebliche Leistungseinbußen mehrmals für dasselbe Modul aufgerufen werden, wenn Folgendes gilt:

  • Der Benutzer besucht eine Komponente, die zum Importieren eines Moduls JSHost.ImportAsync aufruft, er navigiert von der Komponente weg und kehrt später zur Komponente zurück, wobei JSHost.ImportAsync erneut für den gleichen Modulimport aufgerufen wird.
  • Dasselbe Modul wird von verschiedenen Komponenten verwendet und von JSHost.ImportAsync in jede dieser Komponenten geladen.

Verwenden eines einzelnen JavaScript-Moduls für verschiedene Komponenten

Bevor Sie die Anleitungen in diesem Abschnitt befolgen, sollten Sie die Abschnitte Aufrufen von JavaScript über .NET und Aufrufen von .NET über JavaScript dieses Artikels lesen, die allgemeine Anleitungen zur [JSImport]/[JSExport]-Interoperabilität bereitstellen.

Das Beispiel in diesem Abschnitt zeigt, wie Sie JS-Interoperabilität über ein freigegebenes JS-Modul in einer clientseitigen App verwenden. Die Anleitungen in diesem Abschnitt gelten nicht für Razor-Klassenbibliotheken (RCLs).

Die folgenden Komponenten, Klassen, C#-Methoden und JS-Funktionen werden verwendet:

  • Interop-Klasse (Interop.cs): Richtet mit den Attributen [JSImport] und [JSExport] für ein Modul namens InteropJS-Interoperabilität zum Importieren und Exportieren ein.
    • GetWelcomeMessage: .NET-Methode, die die importierte getMessageJS-Funktion aufruft.
    • SetWelcomeMessage: .NET-Methode, die die importierte setMessageJS-Funktion aufruft.
    • GetMessageFromDotnet: Eine exportierte C#-Methode, die eine Zeichenfolge mit einer Willkommensnachricht zurückgibt, wenn sie über JS aufgerufen wird.
  • wwwroot/js/interop.js-Datei: Enthält die JS-Funktionen.
    • getMessage: Gibt eine Willkommensnachricht zurück, wenn sie von C#-Code in einer Komponente aufgerufen wird.
    • setMessage: Ruft die C#-Methode GetMessageFromDotnet auf und weist die zurückgegebene Willkommensnachricht einem DOM-Element <span> zu.
  • Program.cs ruft JSHost.ImportAsync aus, um das Moduls aus wwwroot/js/interop.js zu laden.
  • CallJavaScript2-Komponente (CallJavaScript2.razor): Ruft GetWelcomeMessage auf und zeigt die zurückgegebene Willkommensnachricht in der Benutzeroberfläche der Komponente an.
  • CallDotNet2 Komponente (CallDotNet2.razor): Ruft SetWelcomeMessage auf.

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

Im vorherigen Beispiel lautet der Namespace der App BlazorSample, und der vollständige Namespace für C#-Interoperabilitätsklassen lautet 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();
}

Machen Sie den Namespace System.Runtime.InteropServices.JavaScript am Anfang der Program.cs-Datei verfügbar:

using System.Runtime.InteropServices.JavaScript;

Laden Sie das Modul in Program.cs, bevor WebAssemblyHost.RunAsync aufgerufen wird:

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

Wichtig

Im Beispiel dieses Abschnitts wird die JS-Interoperabilität verwendet, um ein DOM-Element rein zu Demonstrationszwecken zu mutieren, nachdem die Komponente in OnAfterRender gerendert wurde. Normalerweise sollten Sie das DOM mit JS nur mutieren, wenn das Objekt nicht mit Blazor interagiert. Der in diesem Abschnitt gezeigte Ansatz ähnelt Fällen, in denen eine JS-Bibliothek von einem Drittanbieter in einer Razor-Komponente verwendet wird, wobei die Komponente über JS-Interoperabilität mit der JS-Bibliothek interagiert, die JS-Bibliothek des Drittanbieters mit einem Teil des DOM interagiert und Blazor nicht direkt an den DOM-Updates für diesen Teil des DOM beteiligt ist. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität).

Zusätzliche Ressourcen