JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität)

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. 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.

Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Eine Blazor-App kann JavaScript-(JS)-Funktionen über .NET-Methoden und .NET-Methoden über JS-Funktionen aufrufen. Diese Szenarien werden als JavaScript-Interoperabilität (JS Interop) bezeichnet.

Weitere JS-Interop-Anleitungen finden Sie in den folgenden Artikeln:

Hinweis

Die JavaScript-Interop-API [JSImport]/[JSExport] ist für clientseitige Komponenten in ASP.NET Core in .NET 7 oder höher verfügbar.

Weitere Informationen finden Sie unter JavaScript-Interoperabilität mit JSImport/JSExport mit ASP.NET Core-Blazor.

Komprimierung für interaktive Serverkomponenten mit nicht vertrauenswürdigen Daten

Mit standardmäßiger aktivierter Komprimierung vermeiden Sie die Erstellung sicherer (authentifizierter/autorisierter) serverseitiger Komponenten, die Daten aus nicht vertrauenswürdigen Quellen rendern. Nicht vertrauenswürdige Quellen umfassen Routenparameter, Abfragezeichenfolgen, Daten aus JS-Interoperabilität und andere Datenquellen, die ein Drittbenutzer steuern kann (Datenbanken, externe Dienste). Weitere Informationen finden Sie unter ASP.NET Core BlazorSignalR Anleitungen und Anleitung zur Risikominderung für ASP.NET Core Blazor interaktives serverseitiges Rendering.

Paket mit JavaScript-Interop-Abstraktionen und -Features

Das Paket @microsoft/dotnet-js-interop (npmjs.com) (NuGet-Paket Microsoft.JSInterop) enthält Abstraktionen und Features für die Interoperabilität zwischen .NET- und JavaScript-Code (JS). Die Referenzquelle finden Sie im GitHub-Repository dotnet/aspnetcore (im Ordner /src/JSInterop). Weitere Informationen finden Sie im GitHub-Repository in der Datei README.md.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Weitere Ressourcen zum Schreiben von JS-Interop-Skripts in TypeScript:

Interaktion mit dem DOM

Mutieren Sie das DOM nur mit JavaScript (JS), wenn das Objekt nicht mit Blazor interagiert. Blazor verwaltet Darstellungen des DOM und interagiert direkt mit DOM-Objekten. Wenn ein von Blazor gerendertes Element extern mithilfe von JS direkt oder über JS-Interop geändert wird, stimmt das DOM möglicherweise nicht mehr der internen Darstellung von Blazor überein, sodass es zu einem undefinierte Verhalten kommen kann. Ein undefiniertes Verhalten kann lediglich die Darstellung von Elementen oder deren Funktionen beeinträchtigen, aber auch Sicherheitsrisiken für die App oder den Server mit sich bringen.

Diese Anleitung gilt nicht nur für Ihren eigenen JS-Interop-Code, sondern auch für alle JS-Bibliotheken, die von der App verwendet werden, einschließlich aller von einem Drittanbieterframework bereitgestellten Inhalte wie Bootstrap JS und jQuery.

In einigen Dokumentationsbeispielen wird JS-Interop verwendet, um ein Element als Teil eines Beispiels lediglich zu Demonstrationszwecken zu mutieren. In diesen Fällen wird im Text eine Warnung angezeigt.

Weitere Informationen finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

JavaScript-Klasse mit einem Feld vom Typ „Funktion“

Eine JavaScript-Klasse mit einem Feld vom Typ Funktion wird von BlazorJS-Interoperabilität nicht unterstützt. Verwenden Sie Javascript-Funktionen in Klassen.

Nicht unterstützt:GreetingHelpers.sayHello wird in der folgenden Klasse als Feld vom Typ „Funktion“ von JS Interop von Blazor nicht erkannt und kann nicht von C#-Code aus ausgeführt werden:

export class GreetingHelpers {
  sayHello = function() {
    ...
  }
}

Unterstützt:GreetingHelpers.sayHello wird in der folgenden Klasse als Funktion unterstützt:

export class GreetingHelpers {
  sayHello() {
    ...
  }
}

Pfeilfunktionen werden ebenfalls unterstützt:

export class GreetingHelpers {
  sayHello = () => {
    ...
  }
}

Vermeiden von Inlineereignishandlern

Eine JavaScript-Funktion kann direkt aus einem Inlineereignishandler aufgerufen werden. Im folgenden Beispiel ist alertUser eine JavaScript-Funktion, die aufgerufen wird, wenn die Schaltfläche von den Benutzenden ausgewählt wird:

<button onclick="alertUser">Click Me!</button>

Die Verwendung von Inlineereignishandlern ist jedoch eine schlechte Designentscheidung für den Aufruf von JavaScript-Funktionen:

Es wird empfohlen, Inlineereignishandler zu vermeiden und stattdessen Ansätze zu verwenden, bei denen Handler in JavaScript mit addEventListener zugewiesen werden, wie das folgende Beispiel zeigt:

AlertUser.razor.js:

export function alertUser() {
  alert('The button was selected!');
}

export function addHandlers() {
  const btn = document.getElementById("btn");
  btn.addEventListener("click", alertUser);
}

AlertUser.razor:

@page "/alert-user"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>Alert User</h1>

<p>
    <button id="btn">Click Me!</button>
</p>

@code {
    private IJSObjectReference? module;

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./Components/Pages/AlertUser.razor.js");

            await module.InvokeVoidAsync("addHandlers");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

Weitere Informationen finden Sie in den folgenden Ressourcen:

Asynchrone JavaScript-Aufrufe

JS Interop-Aufrufe sind asynchron, unabhängig davon, ob der aufgerufene Code synchron oder asynchron ist. Die Aufrufe sind asynchron, um sicherzustellen, dass die Komponenten mit den serverseitigen und clientseitigen Rendering-Modellen kompatibel sind. Bei Verwendung von serverseitigem Rendering müssen JS-Interop-Aufrufe asynchron sein, da sie über eine Netzwerkverbindung gesendet werden. Für Apps, die ausschließlich clientseitiges Rendering verwenden, werden synchrone JS-Interop-Aufrufe unterstützt.

Objektserialisierung

Blazor verwendet System.Text.Json für die Serialisierung. Dabei gelten die folgenden Anforderungen und Standardverhalten:

  • Typen müssen über einen Standardkonstruktor verfügen, get/set-Zugriffsmethoden müssen öffentlich sein, und Felder werden nie serialisiert.
  • Die globale Standardserialisierung kann nicht angepasst werden, damit vorhandene Komponentenbibliotheken ihre Funktionalität nicht verlieren, die Leistung und Sicherheit nicht beeinträchtigt werden und die Zuverlässigkeit nicht verringert wird.
  • Das Serialisieren von .NET-Membernamen führt zu JSON-Schlüsselnamen in Kleinbuchstaben.
  • ON-Code wird als JsonElement-C#-Instanzen deserialisiert, wodurch eine gemischte Groß- und Kleinschreibung möglich wird. Die interne Umwandlung für die Zuweisung zu C#-Modelleigenschaften funktioniert trotz aller Unterschiede zwischen JSON-Schlüsselnamen und C#-Eigenschaftsnamen erwartungsgemäß.
  • Komplexe Frameworktypen, wie z. B. KeyValuePair können vom IL Trimmer beim Veröffentlichen entfernt und nicht für die JS-Interoperabilität vorhanden sein. Wir empfehlen, benutzerdefinierte Typen für Typen zu erstellen, die der IL Trimmer entfernt.
  • Blazor basiert immer auf Reflexion für die JSON-Serialisierung, einschließlich der Verwendung der C#-Quellgenerierung. Das Festlegen von JsonSerializerIsReflectionEnabledByDefault auf false in der Projektdatei der App führt zu einem Fehler, wenn versucht wird, die Serialisierung durchzuführen.

Die benutzerdefinierte Serialisierung der JsonConverter-API ist möglich. Eigenschaften können mit einem [JsonConverter]-Attribut versehen werden, um die Standardserialisierung eines vorhandenen Datentyps zu überschreiben.

Weitere Informationen finden Sie in den folgenden Ressourcen der .NET-Dokumentation:

Blazor unterstützt optimiertes Bytearray-JS-Interop, mit dem sich das Codieren/Decodieren von Bytearrays in Base64 vermeiden lässt. Die App kann die benutzerdefinierte Serialisierung anwenden und die resultierenden Bytes übergeben. Weitere Informationen finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

Blazor unterstützt das Rückgängigmachen von JS-Interop-Marshalling, wenn große Mengen an .NET-Objekten schnell serialisiert werden müssen oder wenn große oder viele .NET-Objekte serialisiert werden müssen. Weitere Informationen finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten

Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen auf dem Client das MutationObserver-Muster in JavaScript (JS):

  • Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in Dispose{Async} aus dem DOM entfernt.
  • Beim serverseitigen Rendering wurde der Blazor-Renderer möglicherweise bereits durch das Framework verworfen, wenn der Bereinigungscode in Dispose{Async} ausgeführt wird.

Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Im folgenden Beispiel gilt für die DOMCleanup-Komponente:

  • Sie enthält ein <div>-Element mit cleanupDiv als id. Das <div>-Element wird zusammen mit dem rest des DOM-Markup der Komponente aus dem DOM entfernt, wenn die Komponente aus dem DOM entfernt wird.
  • Lädt die DOMCleanup-JS-Klasse aus der Datei DOMCleanup.razor.js und ruft die createObserver-Funktion auf, um den MutationObserver-Rückruf einzurichten. Diese Aufgaben werden in der OnAfterRenderAsync-Lebenszyklusmethode durchgeführt.

DOMCleanup.razor:

@page "/dom-cleanup"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>DOM Cleanup Example</h1>

<div id="cleanupDiv"></div>

@code {
    private IJSObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./Components/Pages/DOMCleanup.razor.js");

            await module.InvokeVoidAsync("DOMCleanup.createObserver");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

Im folgenden Beispiel wird der MutationObserver-Rückruf jedes Mal ausgeführt, wenn eine DOM-Änderung erfolgt. Führen Sie den Bereinigungscode aus, wenn die if-Anweisung bestätigt, dass das Zielelement (cleanupDiv) entfernt wurde (if (targetRemoved) { ... }). Es ist wichtig, MutationObserver zu trennen und zu löschen, um nach Ausführung des Bereinigungscodes einen Arbeitsspeicherverlust zu vermeiden.

DOMCleanup.razor.js zusammen mit der DOMCleanup-Komponente von weiter oben:

export class DOMCleanup {
  static observer;

  static createObserver() {
    const target = document.querySelector('#cleanupDiv');

    this.observer = new MutationObserver(function (mutations) {
      const targetRemoved = mutations.some(function (mutation) {
        const nodes = Array.from(mutation.removedNodes);
        return nodes.indexOf(target) !== -1;
      });

      if (targetRemoved) {
        // Cleanup resources here
        // ...

        // Disconnect and delete MutationObserver
        this.observer && this.observer.disconnect();
        delete this.observer;
      }
    });

    this.observer.observe(target.parentNode, { childList: true });
  }
}

window.DOMCleanup = DOMCleanup;

JavaScript-Interopaufrufe ohne Verbindung

Dieser Abschnitt gilt nur für serverseitige Apps.

JavaScript-Interopaufrufe (JS) können nicht ausgegeben werden, nachdem eine SignalR-Verbindung getrennt wurde. Ohne eine Verbindung während der Komponentenbereinigung oder zu jedem anderen Zeitpunkt, an dem keine Verbindung vorhanden ist, schlagen die folgenden Methodenaufrufe fehl und protokollieren eine Meldung, dass die Verbindung als JSDisconnectedException getrennt wurde:

Um die Protokollierung von JSDisconnectedException zu vermeiden oder benutzerdefinierte Informationen zu protokollieren, fangen Sie die Ausnahme in einer try-catch-Anweisung ab.

Für das folgende Beispiel für Komponbereinigung gilt:

  • Die Komponente implementiert IAsyncDisposable.
  • objInstance ist eine IJSObjectReference.
  • JSDisconnectedException wird abgefangen und nicht protokolliert.
  • Optional können Sie benutzerdefinierte Informationen in der catch-Anweisung auf beliebiger Protokollebene protokollieren. Im folgenden Beispiel werden keine benutzerdefinierten Informationen protokolliert, da davon ausgegangen wird, dass es dem Entwickler gleichgültig ist, wann oder wo Verbindungen während der Komponentenbereinigung getrennt werden.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (objInstance is not null)
        {
            await objInstance.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Wenn Sie ihre eigenen JS-Objekte bereinigen oder anderen JS-Code auf dem Client ausführen müssen, nachdem eine Verbindung verloren gegangen ist, verwenden Sie das MutationObserver-Muster in JS auf dem Client. Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Weitere Informationen finden Sie in den folgenden Artikeln:

Zwischengespeicherte JavaScript-Dateien

JavaScript-Dateien (JS) und andere statische Objekte werden in der Regel während der Entwicklung in der Development-Umgebung nicht auf Clients zwischengespeichert. Während der Entwicklung enthalten statische Ressourcenanforderungen den Cache-Control-Header mit einem Wert von no-cache oder max-age mit einem Wert 0 (0).

Während der Produktion in der Production-Umgebung werden JS-Dateien in der Regel von Clients zwischengespeichert.

Um das clientseitige Zwischenspeichern in Browsern zu deaktivieren, verwenden Entwickler in der Regel einen der folgenden Ansätze:

  • Deaktivieren Sie die Zwischenspeicherung, wenn die Entwicklertoolkonsole des Browsers geöffnet ist. Anleitungen finden Sie in der Dokumentation zur Entwicklertools der einzelnen Browser-Maintainer:
  • Führen Sie eine manuelle Browseraktualisierung einer Webseite der App Blazor durch, um JS-Dateien erneut vom Server zu laden. Die HTTP-Middleware zur Zwischenspeicherung von ASP.NET Core verwendet immer einen gültigen Cache-Control-Header (kein Caching), der von einem Client gesendet wird.

Weitere Informationen finden Sie unter:

Größenbeschränkungen bei JavaScript-Interop-Aufrufen

Dieser Abschnitt gilt nur für interaktive Komponenten in serverseitigen Apps. Bei clientseitigen Komponenten erzwingt das Framework keine Einschränkungen hinsichtlich der Größe von JS-Interop-Eingaben und -Ausgaben.

Bei interaktiven Komponenten in serverseitigen Apps wird die Größe von JS-Interop-Aufrufen, die Daten vom Client an den Server übergeben, durch die maximale Größe eingehender SignalR-Nachrichten beschränkt, die für Hubmethoden zulässig sind. Dies wird durch HubOptions.MaximumReceiveMessageSize erzwungen (Standardwert: 32 KB). SignalR-Nachrichten von JS an .NET, die größer als MaximumReceiveMessageSize sich, lösen einen Fehler aus. Das Framework beinhaltet keine Beschränkungen hinsichtlich der Größe einer SignalR-Nachricht vom Hub an einen Client. Weitere Informationen zum Größenlimit sowie Fehlermeldungen und Anleitungen zum Umgang mit Nachrichtengrößenbeschränkungen finden Sie im ASP.NET Core-Blazor-SignalR-Leitfaden unter Maximale Größe für empfangene Nachrichten.

Ermitteln, wo die App ausgeführt wird

Wenn es für die App relevant ist, zu wissen, wo Code für JS-Interopaufrufe ausgeführt wird, verwenden Sie OperatingSystem.IsBrowser, um festzustellen, ob die Komponente im Kontext des Browsers in WebAssembly ausgeführt wird.