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:
- Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor
- Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor
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:
- TypeScript
- Esercitazione: Creare un'app ASP.NET Core con TypeScript in Visual Studio
- Gestire pacchetti npm in Visual Studio
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:
- La combinazione di markup HTML e codice JavaScript spesso comporta codice non gestibile.
- L'esecuzione del gestore eventi inline può essere bloccata da un CSP (Content Security Policy) (documentazione MDN).
È 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 .
Per altre informazioni, vedere gli articoli seguenti:
Per altre informazioni, vedere Chiamare funzioni JavaScript da metodi .NET in ASP.NET Core Blazor.
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
JsonSerializerIsReflectionEnabledByDefault
false
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:
- Serializzazione e deserializzazione JSON (marshalling e unmarshalling) in .NET
-
Come personalizzare nomi e valori di proprietà con
System.Text.Json
- Come scrivere convertitori personalizzati per la serializzazione JSON in .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 unid
dicleanupDiv
. L'elemento<div>
viene rimosso dal DOM insieme al resto del markup DOM del componente quando il componente viene rimosso dal DOM. - Carica la
DOMCleanup
JS classe dalDOMCleanup.razor.js
file e chiama la relativacreateObserver
funzione per configurare ilMutationObserver
callback. Queste attività vengono eseguite nelOnAfterRenderAsync
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
- 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 edisconnectedCallback
. 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:
- JS chiamate di metodo di interoperabilità
-
Dispose
/DisposeAsync
chiama qualsiasi IJSObjectReference.
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:
- Chrome DevTools (Strumenti di sviluppo di Chrome)
- Panoramica degli strumenti di sviluppo di Microsoft Edge
- 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.