Dela via


ASP.NET Core Blazor JavaScript-samverkan (JS interop)

Notera

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln.

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den nuvarande utgåvan hittas i .NET 9 versionen av den här artikeln.

Viktig

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

En Blazor app kan anropa JavaScript-funktioner (JS) från .NET-metoder och .NET-metoder från JS funktioner. Dessa scenarier kallas JavaScript-samverkan (JS interop).

Ytterligare riktlinjer för JS interoperabilitet finns i följande artiklar:

Not

JavaScript [JSImport]/[JSExport] interop API är tillgängligt för komponenter på klientsidan i ASP.NET Core i .NET 7 eller senare.

Mer information finns i JavaScript JSImport/JSExport interop med ASP.NET Core Blazor.

Komprimering för interaktiva serverkomponenter med ej betrodda data

Med komprimering, som är aktiverad som standard, undviker du att skapa säkra (autentiserade/auktoriserade) interaktiva komponenter på serversidan som återger data från ej betrodda källor. Ej betrodda källor omfattar routningsparametrar, frågesträngar, data från JS interop och alla andra datakällor som en användare från tredje part kan kontrollera (databaser, externa tjänster). Mer information finns i vägledningen ASP.NET Core BlazorSignalR och Threat mitigation guidance for ASP.NET Core Blazor interactive server-side rendering.

JavaScript-interopabstraktioner och funktionspaket

@microsoft/dotnet-js-interop-paketet (npmjs.com) (Microsoft.JSInterop NuGet-paket) innehåller abstraktioner och funktioner för interop mellan .NET- och JavaScript-kod (JS). Referenskällan är tillgänglig på dotnet/aspnetcore GitHub-lagringsplats (/src/JSInterop mapp). Mer information finns i GitHub-lagringsplatsens README.md-fil.

Not

Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik version använder du listrutan Växla grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).

Ytterligare resurser för att skriva JS interop-skript i TypeScript:

Interaktion med DOM

Mutera endast DOM med JavaScript (JS) när objektet inte interagerar med Blazor. Blazor upprätthåller representationer av DOM och interagerar direkt med DOM-objekt. Om ett element som återges av Blazor ändras externt med hjälp av JS direkt eller via JS Interop, kanske DOM inte längre matchar Blazorinterna representation, vilket kan resultera i odefinierat beteende. Odefinierat beteende kan bara störa presentationen av element eller deras funktioner, men kan också medföra säkerhetsrisker för appen eller servern.

Den här vägledningen gäller inte bara din egen JS interop-kod utan även för alla JS bibliotek som appen använder, inklusive allt som tillhandahålls av ett ramverk från tredje part, till exempel Bootstrap JS och jQuery.

I några dokumentationsexempel används JS interop för att mutera ett element enbart i demonstrationssyfte som en del av ett exempel. I dessa fall visas en varning i texten.

Mer information finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

JavaScript-klass med ett fält av typen funktion

En JavaScript-klass med ett fält av typen funktion stöds inte av BlazorJS interop. Använd Javascript-funktioner i klasser.

stöds inte:GreetingHelpers.sayHello i följande klass som ett fält av typen funktion identifieras inte av Blazor: s JS-interoperabilitet och kan inte exekveras från C#-kod:

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

✔️ stöds som funktion i följande klass:GreetingHelpers.sayHello

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

Pilfunktioner stöds också:

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

Undvik infogade händelsehanterare

En JavaScript-funktion kan anropas direkt från en infogad händelsehanterare. I följande exempel är alertUser en JavaScript-funktion som anropas när knappen väljs av användaren:

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

Användningen av infogade händelsehanterare är dock ett dåligt designval för att anropa JavaScript-funktioner:

Vi rekommenderar att du undviker infogade händelsehanterare till förmån för metoder som tilldelar hanterare i JavaScript med addEventListener, vilket visas i följande exempel:

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)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }
    }
}

I det föregående exemplet fångas JSDisconnectedException under moduldemontering om Blazor:s SignalR-krets går förlorad. Om föregående kod används i en Blazor WebAssembly app finns det ingen SignalR anslutning att förlora, så du kan ta bort try-catch-blocket och behålla raden som tar bort modulen (await module.DisposeAsync();). Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

Mer information finns i följande resurser:

Asynkrona JavaScript-anrop

JS interop-anrop är asynkrona, oavsett om den anropade koden är synkron eller asynkron. Anrop är asynkrona för att säkerställa att komponenterna är kompatibla över server- och klientsidans återgivningsmodeller. När du använder återgivning på serversidan måste JS interop-anrop vara asynkrona eftersom de skickas via en nätverksanslutning. Synkrona JS interop-anrop stöds för appar som uteslutande använder återgivning på klientsidan.

Objektserialisering

Blazor använder System.Text.Json för serialisering med följande krav och standardbeteenden:

  • Typer måste ha en standardkonstruktor, get/set måste vara offentliga och fälten serialiseras aldrig.
  • Global standard serialisering är inte anpassningsbar för att undvika att bryta befintliga komponentbibliotek, påverka prestanda och säkerhet och minska tillförlitligheten.
  • Serialisering av .NET-medlemsnamn resulterar i gemener av JSON-nyckelnamn.
  • JSON deserialiseras som JsonElement C#-instanser, vilket tillåter blandat hölje. Intern typkonvertering för tilldelning till egenskaper i C#-modell fungerar som förväntat trots eventuella skillnader mellan JSON-nyckelnamn och C#-egenskapsnamn.
  • Komplexa ramverkstyper, till exempel KeyValuePair, kan trimmas bort av IL Trimmer när publiceras och inte finns för JS interop. Vi rekommenderar att du skapar anpassade typer för typer som IL Trimmer trimmar bort.
  • Blazor förlitar sig alltid på reflektion för JSON-serialisering, inklusive när du använder C# källgenerering. Om du anger JsonSerializerIsReflectionEnabledByDefault till false i appens projektfil resulterar det i ett fel när serialisering görs.

JsonConverter API är tillgängligt för anpassad serialisering. Egenskaper kan kommenteras med ett [JsonConverter] attribut för att åsidosätta standard serialisering för en befintlig datatyp.

Mer information finns i följande resurser i .NET-dokumentationen:

Blazor stöder optimerad bytematris JS interop som undviker kodning/avkodning av bytematriser till Base64. Appen kan tillämpa anpassad serialisering och skicka de resulterande byteen. Mer information finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

Blazor stöder odirigerad JS interop när en stor mängd .NET-objekt snabbt serialiseras eller när stora .NET-objekt eller många .NET-objekt måste serialiseras. Mer information finns i Anropa JavaScript-funktioner från .NET-metoder i ASP.NET Core Blazor.

DOM-rensningsuppgifter vid bortskaffande av komponenter

Kör inte JS interop-kod för DOM-rensningsuppgifter under komponentens bortskaffande. Använd i stället MutationObserver-mönstret i JavaScript (JS) på klienten av följande skäl:

  • Komponenten kan ha tagits bort från DOM när rensningskoden körs i Dispose{Async}.
  • Vid serverside-rendering kan Blazor-renderaren ha avyttrats av ramverket vid den tidpunkt då din rensningskod körs i Dispose{Async}.

Med MutationObserver-mönstret kan du köra en funktion när ett element tas bort från DOM.

I följande exempel, komponenten DOMCleanup:

  • Innehåller en <div> med en id av cleanupDiv. Elementet <div> tas bort från DOM tillsammans med resten av komponentens DOM-markering när komponenten tas bort från DOM.
  • Läser in DOMCleanupJS-klassen från DOMCleanup.razor.js-filen och anropar dess createObserver-funktion för att konfigurera MutationObserver återanrop. Dessa uppgifter utförs i OnAfterRenderAsync livscykelmetod.

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)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }
    }
}

I föregående exempel fångas JSDisconnectedException under modulens avveckling om Blazor:s SignalR-kretsen förloras. Om föregående kod används i en Blazor WebAssembly app finns det ingen SignalR anslutning att förlora, så du kan ta bort try-catch-blocket och behålla raden som tar bort modulen (await module.DisposeAsync();). Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

I följande exempel körs MutationObserver återanrop varje gång en DOM-ändring sker. Kör rensningskoden när if-instruktionen bekräftar att målelementet (cleanupDiv) har tagits bort (if (targetRemoved) { ... }). Det är viktigt att koppla från och ta bort MutationObserver för att undvika en minnesläcka när rensningskoden har körts.

DOMCleanup.razor.js placeras sida vid sida med den föregående DOMCleanup komponenten:

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;

Föregående metoder kopplar MutationObserver till target.parentNode, som fungerar tills själva parentNode tas bort från DOM. Det här är ett vanligt scenario, till exempel när du navigerar till en ny sida, vilket gör att hela sidkomponenten tas bort från DOM. I sådana fall rensas inte barnkomponenter som observerar ändringar på sidan korrekt.

Anta inte att det är ett bättre mål att observera document.bodyi stället för target.parentNode. Att observera har prestandakonsekvenser eftersom återkopplingslogik körs för alla DOM-uppdateringar av , oavsett om de har att göra med ditt element. Använd någon av följande metoder:

  • I de fall där du kan identifiera en lämplig överordnad nod att observera använder du MutationObserver med den. Helst är denna förfader begränsad till de ändringar som du vill observera snarare än till document.body.
  • I stället för att använda MutationObserverbör du överväga att använda ett anpassat element och disconnectedCallback. Händelsen utlöses alltid när ditt anpassade element kopplas från, oavsett var det finns i DOM i förhållande till DOM-ändringen.

JavaScript-interopanrop utan krets

Det här avsnittet gäller endast för appar på serversidan.

JavaScript (JS) interop-anrop kan inte utfärdas efter att Blazor:s SignalR-krets är frånkopplad. Utan en krets under komponentens bortskaffande eller vid någon annan tidpunkt som en krets inte finns, anropar följande metod fel och loggar ett meddelande om att kretsen är frånkopplad som en JSDisconnectedException:

För att undvika loggning JSDisconnectedException eller logga anpassad information fångar du undantaget i en try-catch-instruktion.

För följande exempel på bortskaffande av komponenter:

  • Komponenten på serversidan implementerar IAsyncDisposable.
  • module är en IJSObjectReference för en JS-modul.
  • JSDisconnectedException fångas och loggas inte.
  • Du kan också logga anpassad information i catch-instruktionen på vilken loggnivå du vill. I följande exempel loggas inte anpassad information eftersom det förutsätter att utvecklaren inte bryr sig om när eller var kretsar kopplas från under komponentens bortskaffande.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Om du måste rensa dina egna JS objekt eller köra annan JS kod på klienten efter att en krets har förlorats i en Blazor app på serversidan använder du MutationObserver-mönstret i JS på klienten. Med MutationObserver-mönstret kan du köra en funktion när ett element tas bort från DOM.

Mer information finns i följande artiklar:

Cachelagrade JavaScript-filer

JavaScript-filer (JS) och andra statiska tillgångar cachelagras vanligtvis inte på klienter under utveckling i Development miljö. Under utvecklingen inkluderar begäranden om statiska resurser Cache-Control headern med värdet no-cache eller max-age med värdet noll (0).

I Production-miljönunder produktion brukar JS-filer cachelagras av klienter.

För att inaktivera cachelagring på klientsidan i webbläsare använder utvecklare vanligtvis någon av följande metoder:

  • Inaktivera cachelagring när webbläsarens utvecklarverktygskonsol är öppen. Vägledning finns i utvecklarverktygsdokumentationen för varje webbläsarunderhållare:
  • Utför en manuell webbläsaruppdatering av en webbsida i Blazor-appen för att läsa in JS filer från servern igen. ASP.NET Cores HTTP Caching Middleware respekterar alltid ett giltigt Cache-Control- som skickas av en klient.

Mer information finns i:

Storleksbegränsningar för JavaScript-interop-anrop

Det här avsnittet gäller endast interaktiva komponenter i appar på serversidan. För komponenter på klientsidan inför ramverket ingen gräns för storleken på JavaScript (JS) interop-indata och utdata.

För interaktiva komponenter i appar på serversidan är JS interop-anrop som skickar data från klienten till servern begränsade i storlek av den maximala inkommande SignalR meddelandestorlek som tillåts för hubbmetoder, vilket framtvingas av HubOptions.MaximumReceiveMessageSize (standard: 32 KB). JS till .NET SignalR meddelanden större än MaximumReceiveMessageSize utlöser ett fel. Ramverket har ingen gräns för storleken på ett SignalR meddelande från hubben till en klient. Mer information om storleksgräns, felmeddelanden och vägledning om hur du hanterar storleksbegränsningar för meddelanden finns i ASP.NET Core BlazorSignalR vägledning.

Ta reda på var appen körs

Om det är relevant för appen att veta var koden körs för JS interop-anrop använder du OperatingSystem.IsBrowser för att avgöra om komponenten körs i webbläsarens kontext på WebAssembly.