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

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 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 8 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à di importazione/JSesportazione JavaScript JScon ASP.NET CoreBlazor.

Compressione per componenti server interattivi con dati non attendibili

Con la compressione, abilitata per impostazione predefinita, evitare di creare componenti interattivi interattivi (autenticati/autorizzati) che eseguono il rendering dei dati da origini 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 altre informazioni, vedere linee guida ASP.NET Core BlazorSignalR e Linee guida per la mitigazione delle minacce per ASP.NET rendering lato server interattivo CoreBlazor.

Pacchetto di astrazioni e funzionalità di interoperabilità JavaScript

Il @microsoft/dotnet-js-interop pacchetto () (npmjs.comMicrosoft.JSInterop pacchetto NuGet) fornisce astrazioni e funzionalità per l'interoperabilità tra il codice .NET e JavaScript (JS). L'origine di riferimento è disponibile nel repository GitHub (cartella).Reference source is available in the dotnet/aspnetcore GitHub repository (/src/JSInterop folder). Per altre informazioni, vedere il file del README.md repository GitHub.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa 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.

Chiamate JavaScript asincrone

Le chiamate all'interoperabilità JS sono asincrone per impostazione predefinita, indipendentemente dal fatto che il codice chiamato sia sincrono o asincrono. Le chiamate sono asincrone per impostazione predefinita 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 restituisce nomi di chiave JSON in lettere minuscole.
  • JSON viene deserializzato come istanze C# di JsonElement, che consentono di usare lettere maiuscole e minuscole. Il cast interno per l'assegnazione alle proprietà del modello C# funziona come previsto, nonostante le differenze nell'uso di maiuscole e minuscole tra i nomi delle chiavi JSON e i nomi delle proprietà C#.
  • I tipi di framework complessi, ad esempio KeyValuePair, potrebbero essere eliminati dal trimmer IL durante la pubblicazione e non presenti per JS l'interoperabilità. Per impostazione predefinita, è consigliabile creare tipi personalizzati per i tipi tagliati dal trimmer IL.

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 JS codice di interoperabilità per le attività di pulizia 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, il Blazor renderer potrebbe essere stato eliminato dal framework al momento dell'esecuzione del codice di pulizia in Dispose{Async}.

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

Nell'esempio seguente il DOMCleanup componente :

  • Contiene un <div> oggetto con un id oggetto 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 metodo del OnAfterRenderAsync 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)
        {
            await module.DisposeAsync();
        }
    }
}

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 disconnettersi ed eliminare per MutationObserver evitare una perdita di memoria dopo l'esecuzione del codice di pulizia.

DOMCleanup.razor.js posizionato side-by-side con il componente precedente DOMCleanup :

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;

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 la disconnessione di un SignalR circuito. 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 JSDisconnectedException o registrare informazioni personalizzate, intercettare l'eccezione in un'istruzione try-catch .

Per l'esempio di eliminazione dei componenti seguente:

  • Il componente implementa IAsyncDisposable.
  • objInstance è di tipo IJSObjectReference.
  • 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 (objInstance is not null)
        {
            await objInstance.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Se è necessario pulire i propri JS oggetti o eseguire altro JS codice nel client dopo la perdita di un circuito, usare il MutationObserver modello in JS nel client. Il MutationObserver modello consente di eseguire una funzione quando un elemento viene rimosso dal DOM.

Per altre informazioni, vedere gli articoli seguenti:

  • Gestire gli errori nelle app di base ASP.NETBlazor: la sezione Interoperabilità JavaScript illustra la gestione degli errori negli JS scenari di interoperabilità.
  • ASP.NET ciclo di vita dei componenti principaliRazor: la sezione Eliminazione dei componenti con IDisposable e IAsyncDisposable descrive come implementare i modelli di eliminazione nei Razor componenti.

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 di memorizzazione nella cache HTTP di ASP.NET Core rispetta sempre un'intestazione Cache-Control no-cache valida 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 in base alle dimensioni massime SignalR consentite per i metodi hub, che vengono applicate per impostazione HubOptions.MaximumReceiveMessageSize predefinita: 32 KB. JS per i messaggi .NET SignalR di dimensioni maggiori rispetto MaximumReceiveMessageSize a generare 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 dimensioni, i messaggi di errore e le indicazioni sulla gestione dei limiti delle dimensioni dei messaggi, vedere ASP.NET Linee guida di baseBlazorSignalR.

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.