Sdílet prostřednictvím


Interoperabilita JavaScriptu v ASP.NET Core Blazor (interop JS)

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.

Varování

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Pokud chcete aktuální verzi, podívejte se na .NET 9 verzi tohoto článku.

Aplikace Blazor může volat funkce JavaScriptu (JS) z metod .NET a metody .NET z funkcí JS. Tyto scénáře se označují jako interoperabilita JavaScriptu (JS interop).

Další pokyny pro interoperabilitu JS jsou uvedeny v následujících článcích:

Poznámka:

Rozhraní API pro interoperabilitu JavaScriptu [JSImport]/[JSExport] je k dispozici pro klientské komponenty v technologii ASP.NET Core ve verzích .NET 7 a novějších.

Další informace viz JavaScript JSImport/JSExport interop s ASP.NET Core Blazor.

Komprese pro interaktivní součásti serveru s nedůvěryhodnými daty

Díky kompresi, která je ve výchozím nastavení povolená, se vyhněte vytváření zabezpečených (ověřených/autorizovaných) interaktivních komponent na straně serveru, které vykreslují data z nedůvěryhodných zdrojů. Mezi nedůvěryhodné zdroje patří parametry směrování, řetězce dotazů, data z JS interoperability a jakýkoli jiný zdroj dat, který může uživatel třetí strany řídit (databáze, externí služby). Další informace najdete v pokynech BlazorSignalRa na straně serveru ASP.NET Core.Blazor

Balíček abstrakcí a funkcí pro interoperabilitu s JavaScriptem

Balíček @microsoft/dotnet-js-interop (npmjs.com) (Microsoft.JSInteropbalíček NuGet) poskytuje abstrakce a funkce pro interoperabilitu mezi kódem .NET a JavaScript (JS). Referenční zdroj je k dispozici v dotnet/aspnetcore úložišti GitHub (/src/JSInterop složka). Další informace najdete v souboru úložiště README.md GitHub.

Poznámka:

Odkazy na dokumentaci k referenčnímu zdroji .NET obvykle načítají výchozí větev úložiště, která představuje aktuální vývoj pro příští verzi .NET. Pokud chcete vybrat značku pro konkrétní verzi, použijte rozbalovací seznam pro přepnutí větví nebo značek. Další informace najdete v tématu Jak vybrat značku verze zdrojového kódu ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Další zdroje pro psaní JS interop skriptů v TypeScriptu:

Interakce s DOM

Pouze změňte DOM pomocí JavaScriptu (JS) když objekt neinteraguje s Blazor. Blazor udržuje reprezentaci modelu DOM a interaguje přímo s objekty DOM. Pokud se prvek vykreslený architekturou Blazor externě upraví přímo pomocí JS nebo prostřednictvím zprostředkovatele komunikace JS, nemusí se model DOM už shodovat s interní reprezentací v architektuře Blazor, což může vést k nedefinovanému chování. Nedefinované chování může narušovat prezentaci prvků nebo jejich funkcí, ale může také představovat bezpečnostní rizika pro aplikaci nebo server.

Tyto doprovodné materiály se vztahují nejen na váš vlastní kód zprostředkovatele komunikace JS, ale také na všechny knihovny JS, které aplikace používá, včetně čehokoli poskytovaného architekturou třetích stran, jako je například Bootstrap JS a jQuery.

V několika dokumentačních příkladech se zprostředkovatel komunikace JS používá k mutaci elementu výhradně pro demonstrační účely jako součást příkladu. V těchto případech se v textu zobrazí upozornění.

Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

JavaScript třída s polem typu funkce

Interoperabilita nepodporuje třídu JavaScriptu s polem typu funkce BlazorJS. Používejte javascriptové funkce ve třídách.

Nepodporováno:GreetingHelpers.sayHello V následující třídě není pole typu funkce objeveno zprostředkováním BlazorJS a nelze jej spustit z kódu C#:

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

Podporováno:GreetingHelpers.sayHello Následující třída jako funkce je podporována:

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

Podporují se také funkce šipky:

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

Vyhněte se vnořeným obslužným rutinám událostí

Funkce JavaScriptu může být vyvolána přímo událostí zapsanou v kódu. V následujícím příkladu je javascriptová funkce volána při alertUser výběru tlačítka uživatelem:

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

Použití vložených obslužných rutin událostí je ale špatnou volbou návrhu pro volání funkcí JavaScriptu:

  • Kombinování kódu HTML a kódu JavaScriptu často vede k nedodržitelnému kódu.
  • Spuštění obslužné rutiny vložené události může být blokováno zásadou zabezpečení obsahu (CSP) (podle dokumentace MDN).

Doporučujeme vyhnout se vloženým obslužným rutinám událostí a místo toho použít přístupy, které přiřazují obslužné rutiny v JavaScriptu, addEventListener, jak je vidět na následujícím příkladu:

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

V předchozím příkladu je JSDisconnectedException zachycen při vyřazení modulu v případě ztráty okruhu BlazorSignalR. Pokud se předchozí kód použije v Blazor WebAssembly aplikaci, nedojde ke ztrátě připojeníSignalR, takže můžete odebrat blok try-catch a ponechat řádek, který modul uvolníawait module.DisposeAsync();. Další informace najdete v tématu ASP.NET Core Blazor JavaScript interoperabilita (JS interop).

Další informace naleznete v následujících zdrojích:

Asynchronní volání JavaScriptu

JS interop volání jsou asynchronní, bez ohledu na to, zda je volaný kód synchronní nebo asynchronní. Volání jsou asynchronní, aby se zajistilo, že komponenty jsou kompatibilní napříč modely vykreslování na straně serveru a na straně klienta. Při používání vykreslování na straně serveru musí být JS interop volání asynchronní, protože se odesílají přes síťové připojení. U aplikací, které výhradně používají vykreslování na straně klienta, se podporují synchronní interoperabilní volání JS.

Další informace najdete v následujících článcích:

Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

Serializace objektů

Blazor používá k serializaci System.Text.Json s následujícími požadavky a výchozím chováním:

  • Typy musí mít výchozí konstruktor, přístupové objekty get/set musí být veřejné a pole se nikdy serializují.
  • Globální výchozí serializaci nejde přizpůsobit, aby nedošlo k porušení stávajících knihoven komponent, dopadům na výkon a zabezpečení a snížení spolehlivosti.
  • Serializace názvů členů .NET vede k malým názvům klíčů JSON.
  • JSON je deserializován jako JsonElement instance C#, což umožňuje smíšené velikosti písmen. Interní přetypování vlastností modelu jazyka C# funguje podle očekávání i přes všechny rozdíly mezi názvy klíčů JSON a názvy vlastností jazyka C#.
  • Komplexní typy rozhraní mohou být oříznuty pomocí il trimmeru při publikování a nejsou k dispozici pro JS interop nebo serializace JSON/ deserializace. Doporučujeme vytvořit vlastní typy pro typy, které IL Trimmer ořízne.
  • Blazor vždy spoléhá na reflexi pro serializaci JSON, včetně použití generování zdrojů v C#. Nastavení JsonSerializerIsReflectionEnabledByDefault na false v souboru projektu aplikace způsobí chybu při pokusu o serializaci.

Rozhraní API JsonConverter je k dispozici pro vlastní serializaci. Vlastnosti mohou být opatřeny atributem [JsonConverter] pro úpravu výchozí serializace pro stávající datový typ.

Další informace najdete v následujících zdrojích informací v dokumentaci k .NET:

Blazor podporuje optimalizovanou interoperabilitu s bajtovými poli JS, která zabraňuje kódování a dekódování bajtových polí do Base64. Aplikace může použít vlastní serializaci a předat výsledné bajty. Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

Blazor podporuje nezařazenou komunikaci JS při rychlé serializaci velkého objemu objektů .NET nebo když je potřeba serializovat velké objekty .NET či mnoho objektů .NET. Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

Úlohy čištění DOM během odstraňování komponent

Neprovádějte interoperační kód pro úlohy čištění DOM během likvidace komponenty. Místo toho použijte MutationObserver vzor v JavaScriptu (JS) na klientovi z následujících důvodů:

  • Komponenta mohla být odebrána z DOM v době, kdy se váš čisticí kód spustí v Dispose{Async}.
  • Během vykreslování Blazor na straně serveru může vykreslovací modul být uvolněn rozhraním v době, kdy se kód čištění spustí v Dispose{Async}.

Model MutationObserver umožňuje spustit funkci při odebrání elementu z modelu DOM.

V následujícím příkladu komponenta DOMCleanup :

  • Obsahuje <div> s id ve formátu cleanupDiv. Prvek <div> se odebere z modelu DOM spolu se zbytkem kódu MODELU DOM komponenty při odebrání komponenty z modelu DOM.
  • Načte třídu DOMCleanupJS ze DOMCleanup.razor.js souboru a zavolá její funkci createObserver pro nastavení zpětného volání MutationObserver. Tyto úlohy se provádějí v OnAfterRenderAsync metodě životního cyklu.

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

V předchozím příkladu je JSDisconnectedException zachycen při vyřazení modulu v případě ztráty okruhu BlazorSignalR. Pokud se předchozí kód použije v Blazor WebAssembly aplikaci, nedojde ke ztrátě připojeníSignalR, takže můžete odebrat blok try-catch a ponechat řádek, který modul uvolníawait module.DisposeAsync();. Další informace najdete v tématu ASP.NET Core Blazor JavaScript interoperabilita (JS interop).

V následujícím příkladu MutationObserver se zpětné volání provede pokaždé, když dojde ke změně DOM. Spusťte kód čištění, když příkaz if potvrdí, že cílový element (cleanupDiv) byl odebrán (if (targetRemoved) { ... }). Je důležité odpojit a odstranit MutationObserver, aby nedošlo k úniku paměti po spuštění kódu pro vyčištění.

DOMCleanup.razor.js umístěné vedle předchozí DOMCleanup součásti:

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;

Předchozí přístupy připojují MutationObserver k target.parentNode, což funguje, dokud se parentNode sám neodebere z modelu DOM. Toto je běžný scénář, například při přechodu na novou stránku, která způsobí odebrání celé součásti stránky z modelu DOM. V takových případech se jakékoliv podřízené komponenty, které sledují změny na stránce, správně nevyčistí.

Nepředpokládáme, že pozorování document.bodymísto target.parentNodeje lepším cílem. Pozorování document.body má vliv na výkon, protože logika zpětného volání se provádí pro všechny aktualizace DOM , bez ohledu na to, zda mají něco společného s vaším prvkem nebo ne. Použijte některý z následujících přístupů:

  • V případech, kdy můžete identifikovat vhodný nadřazený uzel k pozorování, použijte MutationObserver s ním. V ideálním případě je tento předek omezen na změny, které chcete sledovat, a nikoli na document.body.
  • Místo použití MutationObserverzvažte použití vlastního elementu a disconnectedCallback. Událost se vždy aktivuje, když je váš vlastní prvek odpojen, bez ohledu na to, kde se nachází v DOM vzhledem ke změně v DOM.

Volání zprostředkovatele komunikace JavaScriptu bez okruhu

Tato část se týká jenom aplikací na straně serveru.

Volání interop JavaScriptu (JS) nelze vydat po odpojení okruhu BlazorSignalR. Pokud okruh neexistuje během odstraňování součástí nebo v jakémkoli jiném časovém okamžiku, následující volání metod selže a zaznamená zprávu, že okruh je odpojen jako JSDisconnectedException.

Abyste se vyhnuli protokolování JSDisconnectedException nebo pro záznam vlastních informací, zachyťte výjimku v příkazu try-catch.

Příklad vyřazení následující součástky:

  • Komponenta na straně serveru implementuje IAsyncDisposable.
  • module je IJSObjectReference pro JS modul.
  • JSDisconnectedException je zachycen a nezaprotokolován.
  • Volitelně můžete vlastní informace v catch příkazu protokolovat na libovolné úrovni protokolu, kterou dáváte přednost. Následující příklad nezapíše vlastní informace, protože předpokládá, že vývojář nezajímá, kdy nebo kde jsou okruhy odpojeny během vyřazení komponent.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Pokud po ztrátě okruhu na serveru na straně aplikace musíte vyčistit vlastní JS objekty nebo na klientovi spustit jiný JS kód, použijte na klientovi vzor Blazor v MutationObserver. Model MutationObserver umožňuje spustit funkci při odebrání elementu z modelu DOM.

Další informace najdete v následujících článcích:

Soubory JavaScriptu uložené v mezipaměti

Soubory JavaScriptu (JS) a další statické prostředky nejsou během vývoje v prostředí Development obecně uložené v mezipaměti na klientech. Požadavky na statické prostředky během vývoje zahrnují hlavičku Cache-Control s hodnotou nebo no-cache s hodnotou nula (max-age).

V produkční fázi jsou v prostředí Production soubory JS obvykle klienty ukládány do mezipaměti.

Pokud vývojáři chtějí v prohlížečích zakázat ukládání do mezipaměti na straně klienta, obvykle využívají jeden z následujících přístupů:

  • Zákaz ukládání do mezipaměti, když je otevřená konzola vývojářských nástrojů prohlížeče. Pokyny najdete v dokumentaci k vývojářským nástrojům každého správce prohlížeče:
  • Proveďte ruční aktualizaci prohlížeče jakékoli webové stránky aplikace Blazor, aby se znovu načetly soubory JS ze serveru. Middleware HTTP Caching pro ASP.NET Core vždy respektuje platnou hlavičku no-cache odeslanou klientem.

Další informace naleznete v tématu:

Omezení velikosti pro volání JavaScript interakce

Tato část se týká jenom interaktivních komponent v aplikacích na straně serveru. U komponent na straně klienta nezavádí architektura žádné omezení na velikost vstupů a výstupů při interakci s JavaScriptemJS.

U interaktivních komponent v aplikacích JS na straně serveru jsou volání pro interoperabilitu, která předávají data z klienta na server, omezena maximální velikostí příchozích SignalR zpráv povolených pro metody hubu, a to podle HubOptions.MaximumReceiveMessageSize (ve výchozím nastavení: 32 kB). JS zprávy .NET SignalR větší než MaximumReceiveMessageSize vyvolávají chybu. Architektura neukládá omezení velikosti SignalR zprávy z centra na klienta. Další informace o limitu velikosti, chybových zprávách a doprovodných materiálech k řešení limitů velikosti zpráv najdete v ASP.NET BlazorSignalR základních doprovodných materiálech.

Určení, kde je aplikace spuštěná

Pokud je důležité, aby aplikace věděla, kde je kód spuštěn pro JS interop volání, použijte OperatingSystem.IsBrowser k určení, zda se komponenta spouští v kontextu prohlížeče na WebAssembly.