Dela via


Bästa praxis för ASP.NET Core JavaScript-samverkan (interop) prestanda

Anmärkning

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 supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Viktigt!

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.

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

Anrop mellan .NET och JavaScript kräver ytterligare omkostnader eftersom:

  • Anrop är asynkrona.
  • Parametrar och returvärden är JSON-serialiserade för att ge en lättförstårlig konverteringsmekanism mellan .NET- och JavaScript-typer.

Dessutom skickas dessa anrop över nätverket för appar på serversidan Blazor .

Undvik alltför detaljerade samtal

Eftersom varje anrop medför vissa omkostnader kan det vara värdefullt att minska antalet anrop. Överväg följande kod, som lagrar en samling objekt i webbläsarens localStorage:

private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
    foreach (var item in items)
    {
        await JS.InvokeVoidAsync("localStorage.setItem", item.Id, 
            JsonSerializer.Serialize(item));
    }
}

I föregående exempel görs ett separat JS interop-anrop för varje objekt. I stället reducerar följande strategi interop till endast ett anrop:

private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
    await JS.InvokeVoidAsync("storeAllInLocalStorage", items);
}

Motsvarande JavaScript-funktion lagrar hela samlingen med objekt på klienten:

function storeAllInLocalStorage(items) {
  items.forEach(item => {
    localStorage.setItem(item.id, JSON.stringify(item));
  });
}

För Blazor WebAssembly appar förbättras prestanda avsevärt genom att slå samman enskilda JS interop-anrop till ett enda anrop vanligtvis endast om komponenten gör ett stort antal JS interop-anrop.

Överväg att använda synkrona anrop

Anropa JavaScript från .NET

Det här avsnittet gäller endast komponenter på klientsidan.

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 i återgivningslägen på serversidan och på klientsidan. På servern måste alla JS interop-anrop vara asynkrona eftersom de skickas via en nätverksanslutning.

Om du vet att komponenten bara körs på WebAssembly kan du välja att göra synkrona JS interop-anrop. Detta har något mindre omkostnader än att göra asynkrona anrop och kan resultera i färre återgivningscykler eftersom det inte finns något mellanliggande tillstånd i väntan på resultat.

Om du vill göra ett synkront anrop från .NET till JavaScript i en komponent på klientsidan, konvertera IJSRuntime till IJSInProcessRuntime för att göra interop-anropet JS:

@inject IJSRuntime JS

...

@code {
    protected override void HandleSomeEvent()
    {
        var jsInProcess = (IJSInProcessRuntime)JS;
        var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
    }
}

När du arbetar med IJSObjectReference i .NET 5 eller senare komponenter på klientsidan kan du använda IJSInProcessObjectReference synkront i stället. IJSInProcessObjectReference implementerar IAsyncDisposable/IDisposable och ska tas bort för skräpinsamling för att förhindra en minnesläcka, vilket visas i följande exempel:

@inject IJSRuntime JS
@implements IDisposable

...

@code {
    ...
    private IJSInProcessObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import", 
                "./scripts.js");
            var value = module.Invoke<string>("javascriptFunctionIdentifier");
        }
    }

    ...

    void IDisposable.Dispose()
    {
        if (module is not null)
        {
            await module.Dispose();
        }
    }
}

I föregående exempel är en JSDisconnectedException inte fångad under modulhantering eftersom det inte finns någon Blazor–SignalR krets i en Blazor WebAssembly app att förlora. Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

Anropa .NET från JavaScript

Det här avsnittet gäller endast komponenter på klientsidan.

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 i återgivningslägen på serversidan och på klientsidan. På servern måste alla JS interop-anrop vara asynkrona eftersom de skickas via en nätverksanslutning.

Om du vet att komponenten bara körs på WebAssembly kan du välja att göra synkrona JS interop-anrop. Detta har något mindre omkostnader än att göra asynkrona anrop och kan resultera i färre återgivningscykler eftersom det inte finns något mellanliggande tillstånd i väntan på resultat.

Om du vill göra ett synkront anrop från JavaScript till .NET i en komponent på klientsidan använder du DotNet.invokeMethod i stället för DotNet.invokeMethodAsync.

Synkrona anrop fungerar om:

  • Komponenten utfördes endast på WebAssembly.
  • Den anropade funktionen returnerar ett värde synkront. Funktionen är inte en async metod och returnerar inte .NET Task eller JavaScript Promise.

Det här avsnittet gäller endast komponenter på klientsidan.

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 i återgivningslägen på serversidan och på klientsidan. På servern måste alla JS interop-anrop vara asynkrona eftersom de skickas via en nätverksanslutning.

Om du vet att komponenten bara körs på WebAssembly kan du välja att göra synkrona JS interop-anrop. Detta har något mindre omkostnader än att göra asynkrona anrop och kan resultera i färre återgivningscykler eftersom det inte finns något mellanliggande tillstånd i väntan på resultat.

Om du vill göra ett synkront anrop från .NET till JavaScript i en komponent på klientsidan, konvertera IJSRuntime till IJSInProcessRuntime för att göra interop-anropet JS:

@inject IJSRuntime JS

...

@code {
    protected override void HandleSomeEvent()
    {
        var jsInProcess = (IJSInProcessRuntime)JS;
        var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
    }
}

När du arbetar med IJSObjectReference i .NET 5 eller senare komponenter på klientsidan kan du använda IJSInProcessObjectReference synkront i stället. IJSInProcessObjectReference implementerar IAsyncDisposable/IDisposable och ska tas bort för skräpinsamling för att förhindra en minnesläcka, vilket visas i följande exempel:

@inject IJSRuntime JS
@implements IDisposable

...

@code {
    ...
    private IJSInProcessObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import", 
                "./scripts.js");
            var value = module.Invoke<string>("javascriptFunctionIdentifier");
        }
    }

    ...

    void IDisposable.Dispose()
    {
        if (module is not null)
        {
            await module.Dispose();
        }
    }
}

I föregående exempel är en JSDisconnectedException inte fångad under modulhantering eftersom det inte finns någon Blazor–SignalR krets i en Blazor WebAssembly app att förlora. Mer information finns i ASP.NET Core Blazor JavaScript-samverkan (JS interop).

Överväg att använda oformatterade anrop

Det här avsnittet gäller endast för Blazor WebAssembly appar.

När du kör på Blazor WebAssembly går det att göra okontrollerade anrop från .NET till JavaScript. Det här är synkrona anrop som inte utför JSON-serialisering av argument eller returvärden. Alla aspekter av minneshantering och översättningar mellan .NET- och JavaScript-representationer är upp till utvecklaren.

Varning

Även om användningen av IJSUnmarshalledRuntime har minst omkostnader av JS interop-lösningar, är de JavaScript-API:er som krävs för att interagera med dessa API:er för närvarande odokumenterade och kan komma att ändras i framtida versioner.

function jsInteropCall() {
  return BINDING.js_to_mono_obj("Hello world");
}
@inject IJSRuntime JS

@code {
    protected override void OnInitialized()
    {
        var unmarshalledJs = (IJSUnmarshalledRuntime)JS;
        var value = unmarshalledJs.InvokeUnmarshalled<string>("jsInteropCall");
    }
}

Använd JavaScript [JSImport]/[JSExport] interop

JavaScript-interop [JSImport]/[JSExport] för Blazor WebAssembly appar ger bättre prestanda och stabilitet över interop-API:et JS i ramverksversioner före ASP.NET Core i .NET 7.

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