Condividi tramite


Interoperabilità JavaScript (interop Blazor) in ASP.NET Core JS

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Un'app Blazor può richiamare le funzioni JavaScript (JS) dai metodi .NET e i metodi .NET dalle funzioni JS. Questi scenari sono chiamati interoperabilità JavaScript (interop JS).

Altre indicazioni sull'interoperabilità JS sono disponibili negli articoli seguenti:

Nota

L'API di interoperabilità JavaScript [JSImport]/[JSExport] è disponibile per i componenti lato client in ASP.NET Core in .NET 7 o versione successiva.

Per altre informazioni, vedere Interoperabilità JSImport/JSExport JavaScript con ASP.NET Core Blazor.

Compressione per componenti server interattivi con dati non attendibili

Con la compressione, abilitata per impostazione predefinita, evitare di creare componenti interattivi lato server (autenticati/autorizzati) che effettuano il rendering dei dati provenienti da fonti non attendibili. Le origini non attendibili includono parametri di route, stringhe di query, dati di JS interoperabilità e qualsiasi altra origine di dati che un utente di terze parti può controllare (database, servizi esterni). Per ulteriori informazioni, consultare le linee guida di ASP.NET Core BlazorSignalR e le indicazioni sulla mitigazione delle minacce per il rendering lato server interattivo di ASP.NET CoreBlazor.

Pacchetto di astrazioni e funzionalità di interoperabilità JavaScript

Il @microsoft/dotnet-js-interop pacchetto (npmjs.com) (Microsoft.JSInterop pacchetto NuGet) fornisce astrazioni e funzionalità per l'interoperabilità tra codice .NET e JavaScript (JS). L'origine di riferimento è disponibile nel repository GitHub (nella cartella). Per ulteriori informazioni, consultare il file README.md del repository GitHub.

Nota

I collegamenti della documentazione all'origine di riferimento di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo attuale della prossima versione di .NET. Per selezionare un tag per una versione specifica, usare il menu a tendina Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Risorse aggiuntive per la scrittura di JS script di interoperabilità in TypeScript:

Interazione con il DOM

Modifica il DOM solo con JavaScript (JS) quando l'oggetto non interagisce con Blazor. Blazor gestisce le rappresentazioni del DOM e interagisce direttamente con gli oggetti DOM. Se un elemento sottoposto a rendering da Blazor viene modificato esternamente usando JS direttamente o tramite l'interoperabilità JS, il DOM potrebbe non corrispondere più alla rappresentazione interna di Blazor, causando un comportamento non definito. Il comportamento non definito può semplicemente interferire con la presentazione degli elementi o delle relative funzioni, ma può anche introdurre rischi di sicurezza per l'app o il server.

Queste linee guida non si applicano solo al codice di interoperabilità JS, ma anche a tutte le librerie JS usate dall'app, incluse quelle fornite da un framework di terze parti, ad esempio Bootstrap JS e jQuery.

In alcuni esempi della documentazione, l'interoperabilità JS viene usata per modificare un elemento esclusivamente per scopi dimostrativi nell'ambito di un esempio. In questi casi viene visualizzato un avviso nel testo.

Per altre informazioni, vedere Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor.

Classe JavaScript con un campo di funzione di tipo

Una classe JavaScript con un campo di funzione di tipo non è supportata dall'interoperabilità BlazorJS . Usare le funzioni Javascript nelle classi.

Non supportato:GreetingHelpers.sayHello nella classe seguente un campo di tipo funzione non viene individuato dall'interoperabilità di BlazorJS e non può essere eseguito dal codice C#:

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

Supportato:GreetingHelpers.sayHello nella classe seguente come funzione è supportata:

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

Sono supportate anche le funzioni freccia:

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

Evitare i gestori di eventi in linea

Una funzione JavaScript può essere richiamata direttamente da un gestore eventi inline. Nell'esempio seguente è alertUser una funzione JavaScript chiamata quando il pulsante viene selezionato dall'utente:

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

Tuttavia, l'uso di gestori eventi inline è una scelta di progettazione scarsa per chiamare le funzioni JavaScript:

È consigliabile evitare gestori eventi inline a favore di approcci che assegnano gestori in JavaScript con addEventListener, come illustrato nell'esempio seguente:

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)
            {
            }
        }
    }
}

Nell'esempio precedente, JSDisconnectedException viene intrappolato durante l'eliminazione del modulo nel caso in cui il circuito di Blazor venga perso. Se il codice precedente viene usato in un'appBlazor WebAssembly, non c'è alcuna connessione da perdereSignalR, quindi è possibile rimuovere il bloccotry-catch e lasciare la riga che elimina il modulo (await module.DisposeAsync();). Per maggiori informazioni, vedere ASP.NET Core interoperabilità JavaScript (interop).

Per ulteriori informazioni, vedi le seguenti risorse:

Chiamate JavaScript asincrone

JS le chiamate di interoperabilità sono asincrone, indipendentemente dal fatto che il codice chiamato sia sincrono o asincrono. Le chiamate sono asincrone per garantire che i componenti siano compatibili tra i modelli di rendering lato server e lato client. Quando si adotta il rendering lato server, JS le chiamate di interoperabilità devono essere asincrone perché vengono inviate tramite una connessione di rete. Per le app che adottano esclusivamente il rendering lato client, sono supportate chiamate di interoperabilità sincrone JS .

Serializzazione degli oggetti

Blazor usa System.Text.Json per la serializzazione con i requisiti e i comportamenti predefiniti seguenti:

  • I tipi devono avere un costruttore predefinito, le funzioni di accesso get/set devono essere pubbliche e i campi non vengono mai serializzati.
  • La serializzazione predefinita globale non è personalizzabile per evitare l'interruzione delle librerie di componenti esistenti, l'impatto sulle prestazioni e sulla sicurezza e la riduzione dell'affidabilità.
  • La serializzazione dei nomi dei membri .NET comporta nomi di chiave JSON minuscoli.
  • JSON viene deserializzato come JsonElement istanze C#, che permettono l'uso di lettere maiuscole e minuscole combinate. Il cast interno per l'assegnazione alle proprietà del modello C# funziona come previsto, nonostante le differenze tra i nomi delle chiavi JSON e i nomi delle proprietà C#.
  • I tipi di framework complessi, come KeyValuePair, potrebbero essere eliminati dal trimmer IL durante la pubblicazione e potrebbero non essere disponibili per l'interoperabilità JS o la serializzazione/deserializzazione JSON. È consigliabile creare tipi personalizzati per i tipi che il IL Trimmer rimuove.
  • Blazorsi basa sempre sulla reflection per la serializzazione JSON, incluso quando si usa la generazione del codice sorgente C#. L'impostazione su JsonSerializerIsReflectionEnabledByDefaultfalse nel file di progetto dell'app genera un errore quando viene tentata la serializzazione.

L'API JsonConverter è disponibile per la serializzazione personalizzata. È possibile annotare le proprietà con un attributo [JsonConverter] per eseguire l'override della serializzazione predefinita per un tipo di dati esistente.

Per altre informazioni, vedere le risorse seguenti nella documentazione di .NET:

Blazor supporta l'interoperabilità JS ottimizzata per le matrici di byte, che evita la codifica/decodifica delle matrici di byte in Base 64. L'app può applicare la serializzazione personalizzata e passare i byte risultanti. Per altre informazioni, vedere Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor.

Blazor supporta l'interoperabilità JS senza marshalling quando un volume elevato di oggetti .NET viene rapidamente serializzato oppure quando è necessario serializzare oggetti .NET di grandi dimensioni o molti oggetti .NET. Per altre informazioni, vedere Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor.

Attività di pulizia DOM durante l'eliminazione dei componenti

Non eseguire il codice di interoperabilità JS per le attività di pulizia del DOM, durante l'eliminazione del componente. Usare invece il MutationObserver modello in JavaScript (JS) nel client per i motivi seguenti:

  • Il componente potrebbe essere stato rimosso dal DOM al momento dell'esecuzione del codice di pulizia in Dispose{Async}.
  • Durante il rendering lato server, è possibile che il renderer Blazor sia stato eliminato dal framework nel momento in cui il tuo codice di pulizia viene eseguito in Dispose{Async}.

Il MutationObserver modello consente di eseguire una funzione quando un elemento viene rimosso dal DOM.

Nell'esempio seguente, il componente DOMCleanup:

  • Contiene un <div> con un id di cleanupDiv. L'elemento <div> viene rimosso dal DOM insieme al resto del markup DOM del componente quando il componente viene rimosso dal DOM.
  • Carica la DOMCleanupJS classe dal DOMCleanup.razor.js file e chiama la relativa createObserver funzione per configurare il MutationObserver callback. Queste attività vengono eseguite nel OnAfterRenderAsync metodo del ciclo di vita.

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)
            {
            }
        }
    }
}

Nell'esempio precedente, JSDisconnectedException viene intrappolato durante l'eliminazione del modulo nel caso in cui il circuito di Blazor venga perso. Se il codice precedente viene usato in un'appBlazor WebAssembly, non c'è alcuna connessione da perdereSignalR, quindi è possibile rimuovere il bloccotry-catch e lasciare la riga che elimina il modulo (await module.DisposeAsync();). Per maggiori informazioni, vedere ASP.NET Core interoperabilità JavaScript (interop).

Nell'esempio seguente il MutationObserver callback viene eseguito ogni volta che si verifica una modifica DOM. Eseguire il codice di pulizia quando l'istruzione if conferma che l'elemento di destinazione (cleanupDiv) è stato rimosso (if (targetRemoved) { ... }). È importante disconnettere ed eliminare il MutationObserver per evitare una perdita di memoria dopo l'esecuzione del codice di pulizia.

DOMCleanup.razor.js posizionato accanto al componente DOMCleanup precedente:

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;

Gli approcci precedenti collegano il MutationObserver a target.parentNode, che funziona fino a quando parentNode stesso non viene rimosso dal DOM. Si tratta di uno scenario comune, ad esempio, quando si passa a una nuova pagina, che causa la rimozione dell'intero componente di pagina dal DOM. In questi casi, qualsiasi componente figlio che osserva le modifiche all'interno della pagina non viene rimosso correttamente.

Non presupporre che l'osservazione di document.body, invece di target.parentNode, sia un obiettivo migliore. L'osservazione di ha implicazioni sulle prestazioni perché la logica di callback viene eseguita per tutti gli aggiornamenti DOM , a prescindere dal fatto che riguardino o meno il tuo elemento. Usare uno degli approcci seguenti:

  • Nei casi in cui è possibile identificare un nodo predecessore appropriato da osservare, usare MutationObserver con esso. Idealmente, questo predecessore ha come ambito le modifiche che si desidera osservare, anziché document.body.
  • Invece di usare MutationObserver, è consigliabile usare un elemento personalizzato e disconnectedCallback. L'evento viene sempre generato quando l'elemento personalizzato viene disconnesso, indipendentemente dalla posizione in cui si trova nel DOM rispetto alla modifica dom.

Chiamate di interoperabilità JavaScript senza circuito

Questa sezione si applica solo alle app lato server.

Le chiamate di interoperabilità JavaScript (JS) non possono essere eseguite dopo che il circuito Blazor di SignalR è stato disconnesso. Senza un circuito durante l'eliminazione di un componente o in qualsiasi altro momento in cui un circuito non esiste, le chiamate al metodo seguenti hanno esito negativo e registrano un messaggio che indica che il circuito viene disconnesso come JSDisconnectedException:

Per evitare la registrazione di JSDisconnectedException o per registrare informazioni personalizzate, intercetta l'eccezione con un'istruzione try-catch.

Per l'esempio di eliminazione dei componenti seguente:

  • Il componente lato server implementa IAsyncDisposable.
  • module è un IJSObjectReference per un JS modulo.
  • JSDisconnectedException viene intercettata e non registrata.
  • Facoltativamente, è possibile registrare informazioni personalizzate nell'istruzione catch a qualsiasi livello di log preferito. L'esempio seguente non registra informazioni personalizzate perché presuppone che lo sviluppatore non si preoccupi quando o dove i circuiti vengono disconnessi durante l'eliminazione del componente.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Se è necessario pulire i propri JS oggetti o eseguire altro JS codice sul client dopo la perdita di un circuito in un'applicazione lato Blazor server, utilizzare il modello MutationObserver in JS sul client. Il MutationObserver modello consente di eseguire una funzione quando un elemento viene rimosso dal DOM.

Per altre informazioni, vedere gli articoli seguenti:

  • Gestione degli errori nelle app ASP.NET Core: la sezione sull'interoperabilità JavaScript illustra la gestione degli errori negli scenari di interoperabilità.
  • ASP.NET Core Razor component disposal: l'articolo descrive come implementare modelli di eliminazione nei componenti Razor.

File JavaScript memorizzati nella cache

I file JavaScript (JS) e gli altri asset statici in genere non vengono memorizzati nella cache dei client durante lo sviluppo nell'ambiente Development. Durante lo sviluppo, le richieste di asset statici includono l'intestazione Cache-Control con il valore no-cache o max-age con un valore pari a zero (0).

Durante la produzione nell'ambiente Production, JS i file vengono in genere memorizzati nella cache dai client.

Per disabilitare la memorizzazione nella cache lato client nei browser, gli sviluppatori adottano in genere uno degli approcci seguenti:

  • Disabilitare la memorizzazione nella cache quando la console degli strumenti di sviluppo del browser è aperta. Le indicazioni sono disponibili nella documentazione degli strumenti di sviluppo di ogni operatore di gestione del browser:
  • Eseguire un aggiornamento manuale del browser per ogni pagina Web dell'app Blazor per ricaricare i file JS dal server. Il middleware HTTP per la cache di ASP.NET Core rispetta sempre una valida intestazione no-cache inviata da un client.

Per altre informazioni, vedi:

Limiti di dimensioni per le chiamate di interoperabilità JavaScript

Questa sezione si applica solo ai componenti interattivi nelle app lato server. Per i componenti lato client, il framework non impone un limite alle dimensioni degli input e degli output di interoperabilità JavaScript (JS).

Per i componenti interattivi nelle app lato server, JS le chiamate di interoperabilità che passano dati dal client al server sono limitate dalla dimensione massima dei messaggi SignalR consentiti per i metodi dell'hub, ciò è imposto da HubOptions.MaximumReceiveMessageSize (impostazione predefinita: 32 KB). JS i messaggi .NET SignalR di dimensioni superiori a MaximumReceiveMessageSize producono un errore. Il framework non impone un limite alle dimensioni di un SignalR messaggio dall'hub a un client. Per altre informazioni sul limite di dimensione, i messaggi di errore e le indicazioni sulla gestione dei limiti di dimensione dei messaggi, vedere le indicazioni su ASP.NET CoreBlazorSignalR.

Determinare dove è in esecuzione l'app

Se è rilevante per l'app sapere dove è in esecuzione il codice per JS le chiamate di interoperabilità, usare OperatingSystem.IsBrowser per determinare se il componente è in esecuzione nel contesto del browser in WebAssembly.