ASP.NET Core Blazor JavaScript-interoperabiliteit (JS interop)
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
Een Blazor-app kan JavaScript-functies (JS) aanroepen vanuit .NET-methoden en .NET-methoden uit JS functies. Deze scenario's worden JavaScript-interoperabiliteit genoemd (JS interop-).
Meer JS interoperabiliteitsrichtlijnen vindt u in de volgende artikelen:
- JavaScript-functies aanroepen vanuit .NET-methoden in ASP.NET Core Blazor
- .NET-methoden aanroepen vanuit JavaScript-functies in ASP.NET Core Blazor
Notitie
JavaScript [JSImport]
/[JSExport]
interop-API is beschikbaar voor onderdelen aan de clientzijde in ASP.NET Core in .NET 7 of hoger.
Zie JavaScript JSImport/JSExport-interop met ASP.NET Core Blazorvoor meer informatie.
Compressie voor interactieve serveronderdelen met niet-vertrouwde gegevens
Met compressie, die standaard is ingeschakeld, vermijdt u het maken van beveiligde (geverifieerde/geautoriseerde) interactieve serveronderdelen die gegevens uit niet-vertrouwde bronnen weergeven. Niet-vertrouwde bronnen omvatten routeparameters, queryreeksen, gegevens uit JS interop en andere gegevensbronnen die een externe gebruiker kan beheren (databases, externe services). Zie ASP.NET Core BlazorSignalR richtlijnen en Beveiligingsmaatregelenrichtlijnen voor ASP.NET Core Blazor interactieve server-side renderingvoor meer informatie.
JavaScript-interopabstracties en -functiespakket
Het @microsoft/dotnet-js-interop
-pakket (npmjs.com
) (Microsoft.JSInterop
NuGet-pakket) biedt abstracties en functies voor interop-tussen .NET- en JavaScript-code (JS). Referentiebron is beschikbaar in de dotnet/aspnetcore
GitHub-opslagplaats (/src/JSInterop
mapje). Zie het README.md
-bestand van de GitHub-opslagplaats voor meer informatie.
Notitie
Documentatiekoppelingen naar .NET-referentiebron laden meestal de standaardbranch van de opslagplaats, die de huidige ontwikkeling vertegenwoordigt voor de volgende release van .NET. Als u een tag voor een specifieke release wilt selecteren, gebruikt u de Switch-vertakkingen of tags vervolgkeuzelijst. Zie Een versietag selecteren van ASP.NET Core-broncode (dotnet/AspNetCore.Docs #26205)voor meer informatie.
Aanvullende bronnen voor het schrijven van JS interopscripts in TypeScript:
- TypeScript-
- Zelfstudie: Een ASP.NET Core-app maken met TypeScript in Visual Studio
- NPM-pakketten beheren in Visual Studio
Interactie met de DOM
Muteer de DOM alleen met JavaScript (JS) wanneer het object niet communiceert met Blazor. Blazor behoudt weergaven van de DOM en communiceert rechtstreeks met DOM-objecten. Als een element dat wordt weergegeven door Blazor extern wordt gewijzigd met behulp van JS rechtstreeks of via JS Interop, komt de DOM mogelijk niet meer overeen met Blazorinterne weergave, wat kan leiden tot niet-gedefinieerd gedrag. Niet-gedefinieerd gedrag kan alleen de presentatie van elementen of hun functies verstoren, maar kan ook beveiligingsrisico's voor de app of server veroorzaken.
Deze richtlijnen zijn niet alleen van toepassing op uw eigen JS interoperabiliteitscode, maar ook op alle JS bibliotheken die door de app worden gebruikt, inclusief alles wat wordt geleverd door een framework van derden, zoals Bootstrap-JS en jQuery-.
In enkele documentatievoorbeelden wordt JS interop gebruikt om een element uitsluitend te muteren voor demonstratiedoeleinden als onderdeel van een voorbeeld. In die gevallen wordt er een waarschuwing weergegeven in de tekst.
Zie JavaScript-functies aanroepen vanuit .NET-methoden in ASP.NET Core Blazorvoor meer informatie.
JavaScript-klasse met een veld van het type functie
Een JavaScript-klasse met een veld van het type functie wordt niet ondersteund door BlazorJS interop. Gebruik Javascript-functies in klassen.
Niet ondersteund:GreetingHelpers.sayHello
in de volgende klasse als veld van het functietype wordt niet gedetecteerd door de interop van BlazorJS en kan niet vanuit C#-code worden uitgevoerd.
export class GreetingHelpers {
sayHello = function() {
...
}
}
Ondersteund:GreetingHelpers.sayHello
in de volgende klasse als een functie wordt ondersteund:
export class GreetingHelpers {
sayHello() {
...
}
}
Pijlfuncties worden ook ondersteund:
export class GreetingHelpers {
sayHello = () => {
...
}
}
Inline gebeurtenis handlers vermijden
Een JavaScript-functie kan rechtstreeks vanuit een inline gebeurtenis-handler worden aangeroepen. In het volgende voorbeeld is alertUser
een JavaScript-functie die wordt aangeroepen wanneer de knop door de gebruiker wordt geselecteerd:
<button onclick="alertUser">Click Me!</button>
Het gebruik van inline gebeurtenis-handlers is echter een slechte ontwerpkeuze voor het aanroepen van JavaScript-functies:
- Het combineren van HTML-opmaak en JavaScript-code leidt vaak tot onbeheerde code.
- Inline-gebeurtenishandleruitvoering kan worden geblokkeerd door een CSP -documentatie (Content Security Policy) (MDN-documentatie).
We raden u aan inline gebeurtenishandlers te vermijden ten gunste van oplossingen die handlers in JavaScript toewijzen met addEventListener
, zoals in het volgende voorbeeld wordt gedemonstreerd.
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)
{
}
}
}
}
In het voorgaande voorbeeld komt JSDisconnectedException vast te zitten tijdens het verwijderen van de module als het SignalR-circuit van Blazoruitvalt. Als de voorgaande code wordt gebruikt in een Blazor WebAssembly-app, is er geen SignalR verbinding verloren gegaan, zodat u het try
-catch
blok kunt verwijderen en de regel kunt verlaten waarmee de module (await module.DisposeAsync();
) wordt verwijderd. Zie ASP.NET Core Blazor JavaScript-interoperabiliteit (JS interop)voor meer informatie.
Zie de volgende bronnen voor meer informatie:
Asynchrone JavaScript-aanroepen
JS interop-aanroepen zijn asynchroon, ongeacht of de aangeroepen code synchroon of asynchroon is. Aanroepen zijn asynchroon om ervoor te zorgen dat onderdelen compatibel zijn tussen renderingmodellen aan de serverzijde en clientzijde. Bij het toepassen van server-side rendering moeten JS interop-aanroepen asynchroon zijn omdat ze via een netwerkverbinding worden verzonden. Voor apps die uitsluitend gebruikmaken van rendering aan de clientzijde, worden synchrone JS interop-aanroepen ondersteund.
Zie de volgende artikelen voor meer informatie:
Zie JavaScript-functies aanroepen vanuit .NET-methoden in ASP.NET Core Blazorvoor meer informatie.
Objectserialisatie
Blazor gebruikt System.Text.Json voor serialisatie met de volgende vereisten en standaardgedrag:
- Typen moeten een standaardconstructor hebben,
get
/set
accessors moeten openbaar zijn en velden worden nooit geserialiseerd. - Globale standaardserialisatie is niet aanpasbaar om te voorkomen dat bestaande onderdeelbibliotheken worden onderbroken, wat invloed heeft op de prestaties en beveiliging, en dat de betrouwbaarheid wordt verminderd.
- Het serialiseren van .NET-lidnamen resulteert in JSON-sleutelnamen in kleine letters.
- JSON wordt gedeserialiseerd als JsonElement C#-exemplaren, waardoor gemengde behuizing mogelijk is. Interne casting voor toewijzing aan C#-modeleigenschappen werkt zoals verwacht, ondanks eventuele verschillen tussen JSON-sleutelnamen en C#-eigenschappennamen.
- Complexe frameworktypen, zoals KeyValuePair, kunnen worden afgekapt door de IL Trimmer bij het publiceren van en niet aanwezig zijn voor JS interop. We raden u aan aangepaste typen te maken voor typen die de IL Trimmer verwijdert.
-
Blazor is altijd afhankelijk van reflectie voor JSON-serialisatie, inclusief bij het gebruik van C# brongeneratie. Als u
JsonSerializerIsReflectionEnabledByDefault
instelt opfalse
in het projectbestand van de app, treedt er een fout op bij het serialiseren.
JsonConverter-API is beschikbaar voor aangepaste serialisatie. Eigenschappen kunnen worden geannoteerd met een [JsonConverter]
kenmerk om standaardserialisatie voor een bestaand gegevenstype te overschrijven.
Zie de volgende bronnen in de .NET-documentatie voor meer informatie:
- JSON-serialisatie en -deserialisatie (marshalling en unmarshalling) in .NET
-
Eigenschapsnamen en -waarden aanpassen met
System.Text.Json
- Aangepaste conversieprogramma's schrijven voor JSON-serialisatie (marshalling) in .NET
Blazor ondersteunt geoptimaliseerde byte-array JS interop die het coderen/decoderen van byte-arrays naar Base64 vermijdt. De app kan aangepaste serialisatie toepassen en de resulterende bytes doorgeven. Zie JavaScript-functies aanroepen vanuit .NET-methoden in ASP.NET Core Blazorvoor meer informatie.
Blazor ondersteunt niet-gemartelde JS interop wanneer een groot aantal .NET-objecten snel wordt geserialiseerd of wanneer grote .NET-objecten of veel .NET-objecten moeten worden geserialiseerd. Zie JavaScript-functies aanroepen vanuit .NET-methoden in ASP.NET Core Blazorvoor meer informatie.
DOM-opschoontaken tijdens het verwijderen van onderdelen
Voer tijdens het verwijderen van onderdelen geen JS interoperabiliteitscode uit voor DOM-opschoontaken. Gebruik in plaats daarvan het MutationObserver
patroon in JavaScript (JS) op de client om de volgende redenen:
- Het onderdeel is mogelijk verwijderd uit de DOM op het moment dat de opschoningscode wordt uitgevoerd in
Dispose{Async}
. - Tijdens server-side rendering is de Blazor renderer mogelijk door het framework verwijderd tegen de tijd dat uw opschooncode wordt uitgevoerd in
Dispose{Async}
.
Met het MutationObserver
patroon kunt u een functie uitvoeren wanneer een element uit de DOM wordt verwijderd.
In het volgende voorbeeld is de DOMCleanup
-component:
- Bevat een
<div>
met eenid
vancleanupDiv
. Het<div>
element wordt verwijderd uit de DOM, samen met de rest van de DOM-markeringen van het onderdeel wanneer het onderdeel wordt verwijderd uit de DOM. - Laadt de
DOMCleanup
JS-klasse uit hetDOMCleanup.razor.js
-bestand en roept de bijbehorendecreateObserver
-functie aan om deMutationObserver
callback in te stellen. Deze taken worden uitgevoerd in deOnAfterRenderAsync
levenscyclusmethode.
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)
{
}
}
}
}
In het vorige voorbeeld wordt JSDisconnectedException vastgelopen tijdens moduleverwijdering als het SignalR circuit van Blazorverloren gaat. Als de voorgaande code wordt gebruikt in een Blazor WebAssembly-app, is er geen SignalR verbinding verloren gegaan, zodat u het try
-catch
blok kunt verwijderen en de regel kunt verlaten waarmee de module (await module.DisposeAsync();
) wordt verwijderd. Zie ASP.NET Core Blazor JavaScript-interoperabiliteit (JS interop)voor meer informatie.
In het volgende voorbeeld wordt de MutationObserver
callback uitgevoerd telkens wanneer een DOM-wijziging plaatsvindt. Voer de opschooncode uit wanneer de if
-instructie bevestigt dat het doelelement (cleanupDiv
) is verwijderd (if (targetRemoved) { ... }
). Het is belangrijk om de verbinding met het MutationObserver
te verbreken en te verwijderen om een geheugenlek te voorkomen nadat de opschooncode is uitgevoerd.
DOMCleanup.razor.js
naast het voorgaande DOMCleanup
onderdeel geplaatst:
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;
De voorgaande benaderingen voegen de MutationObserver
toe aan target.parentNode
, die werkt totdat parentNode
zelf uit de DOM wordt verwijderd. Dit is een veelvoorkomend scenario, bijvoorbeeld wanneer u naar een nieuwe pagina navigeert, waardoor het hele paginaonderdeel uit de DOM wordt verwijderd. In dergelijke gevallen worden onderliggende onderdelen die wijzigingen in de pagina observeren, niet correct opgeschoond.
Stel niet dat het observeren van document.body
, in plaats van target.parentNode
, een beter doel is. Het observeren van document.body
heeft gevolgen voor de prestaties, omdat callbacklogica wordt uitgevoerd voor alle DOM-updates, ongeacht of ze iets te maken hebben met uw element. Gebruik een van de volgende methoden:
- In gevallen waarin u een geschikt bovenliggend knooppunt kunt identificeren om te observeren, gebruikt u
MutationObserver
ermee. Idealiter is deze bovenliggende voorouder afgestemd op de wijzigingen die u wilt observeren, en niet opdocument.body
. - In plaats van
MutationObserver
te gebruiken, kunt u een aangepast element endisconnectedCallback
gebruiken. De gebeurtenis wordt altijd geactiveerd wanneer de verbinding met uw aangepaste element wordt verbroken, ongeacht waar het zich in de DOM bevindt ten opzichte van de DOM-wijziging.
JavaScript-interop-aanroepen zonder circuit
Deze sectie is alleen van toepassing op apps aan de serverzijde.
Interop-aanroepen van JavaScript (JS) kunnen niet worden uitgegeven nadat BlazorSignalR circuit is verbroken. Zonder een circuit, tijdens het verwijderen van componenten of op elk ander moment waarop er geen circuit is, mislukken de volgende methode-aanroepen en wordt er een bericht gelogd dat het circuit is ontkoppeld met code JSDisconnectedException.
- JS interop-methode-aanroepen
-
Dispose
/DisposeAsync
roept alle IJSObjectReferenceaan.
Om te voorkomen dat JSDisconnectedException wordt gelogd of om aangepaste informatie vast te leggen, kunt u de uitzondering in een try-catch
-instructie ondervangen.
Voor het volgende voorbeeld van het verwijderen van onderdelen:
- Het onderdeel aan de serverzijde implementeert IAsyncDisposable.
-
module
is een IJSObjectReference-module voor een JS. - JSDisconnectedException wordt gevangen en niet geregistreerd.
- Desgewenst kunt u aangepaste gegevens in de
catch
-instructie vastleggen op het gewenste logboekniveau. In het volgende voorbeeld worden geen aangepaste gegevens vastgelegd, omdat wordt ervan uitgegaan dat de ontwikkelaar er niet om geeft wanneer of waar circuits worden losgekoppeld tijdens het verwijderen van onderdelen.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Als u uw eigen JS objecten moet opschonen of andere JS code op de client moet uitvoeren nadat een circuit in een Blazor-app aan de serverzijde verloren is gegaan, gebruikt u het MutationObserver
patroon in JS op de client. Met het MutationObserver
patroon kunt u een functie uitvoeren wanneer een element uit de DOM wordt verwijderd.
Zie de volgende artikelen voor meer informatie:
- Fouten afhandelen in ASP.NET Core Blazor-apps: de sectie JavaScript-interop bespreekt foutafhandeling in JS interop-scenario's.
-
ASP.NET levenscyclus van kernonderdelen Razor: In de sectie Component verwijderen met
IDisposable
enIAsyncDisposable
wordt beschreven hoe u verwijderingspatronen in Razor onderdelen implementeert.
JavaScript-bestanden in cache
JavaScript-bestanden (JS) en andere statische assets worden doorgaans niet opgeslagen in de cache van clients tijdens de ontwikkeling in de Development
-omgeving. Tijdens de ontwikkeling bevatten statische asset-aanvragen de -Cache-Control
-header met een waarde van no-cache
of max-age
, die een waarde van nul hebben (0
).
Tijdens de productie in de Production
omgevingworden JS bestanden meestal in de cache opgeslagen door clients.
Om caching aan de clientzijde in browsers uit te schakelen, gebruiken ontwikkelaars meestal een van de volgende methoden:
- Cache uitschakelen wanneer de console voor ontwikkelaarshulpprogramma's van de browser is geopend. Richtlijnen vindt u in de documentatie voor ontwikkelhulpprogramma's van elke browseronderhouder:
- Chrome DevTools
- Overzicht van Microsoft Edge Developer Tools
- Voer een handmatige browservernieuwing uit van een webpagina van de Blazor-app om JS bestanden van de server opnieuw te laden. ASP.NET Core's HTTP Caching Middleware eert altijd een geldige no-cache
Cache-Control
header die door een client is verzonden.
Zie voor meer informatie:
Groottelimieten voor JavaScript-interop-aanroepen
Deze sectie is alleen van toepassing op interactieve onderdelen in apps aan de serverzijde. Voor onderdelen aan de clientzijde legt het framework geen limiet op voor de grootte van JavaScript (JS) interop-invoer en -uitvoer.
Voor interactieve onderdelen in apps aan de serverzijde worden JS interop-aanroepen die gegevens doorgeven van de client aan de server, beperkt tot de maximale binnenkomende SignalR berichtgrootte die is toegestaan voor hubmethoden, die worden afgedwongen door HubOptions.MaximumReceiveMessageSize (standaard: 32 kB). JS naar .NET SignalR berichten die groter zijn dan MaximumReceiveMessageSize zullen een fout veroorzaken. Het framework legt geen limiet op voor de grootte van een SignalR bericht van de hub naar een client. Zie voor meer informatie over de limiet, foutberichten en richtlijnen voor het omgaan met berichtgroottelimieten ASP.NET Core BlazorSignalR richtlijnen.
Bepalen waar de app wordt uitgevoerd
Als het relevant is voor de app om te weten waar code wordt uitgevoerd voor JS interop-aanroepen, gebruik OperatingSystem.IsBrowser om te bepalen of het onderdeel wordt uitgevoerd in de context van een browser op WebAssembly.