Wywoływanie funkcji języka JavaScript z metod .NET w ASP.NET Core BlazorCall JavaScript functions from .NET methods in ASP.NET Core Blazor

Javier Calvarro Nelson, Daniel Roth, Pranav Krishnamoorthyi Luke LathamBy Javier Calvarro Nelson, Daniel Roth, Pranav Krishnamoorthy, and Luke Latham

BlazorAplikacja może wywoływać funkcje języka JavaScript z metod .NET i metod .NET z funkcji języka JavaScript.A Blazor app can invoke JavaScript functions from .NET methods and .NET methods from JavaScript functions. Te scenariusze nazywa się współdziałaniem JavaScript (w programie js Interop).These scenarios are called JavaScript interoperability (JS interop).

W tym artykule opisano wywoływanie funkcji języka JavaScript z platformy .NET.This article covers invoking JavaScript functions from .NET. Aby uzyskać informacje na temat wywoływania metod .NET w języku JavaScript, zobacz Wywoływanie metod .NET z funkcji języka JavaScript w ASP.NET Core Blazor .For information on how to call .NET methods from JavaScript, see Wywoływanie metod .NET z funkcji języka JavaScript w ASP.NET Core Blazor.

Wyświetl lub pobierz przykładowy kod (jak pobrać)View or download sample code (how to download)

Aby wywołać kod JavaScript z platformy .NET, użyj IJSRuntime abstrakcji.To call into JavaScript from .NET, use the IJSRuntime abstraction. Aby wystawić wywołania programu JS Interop, wstrzyknąć IJSRuntime streszczenie w składniku.To issue JS interop calls, inject the IJSRuntime abstraction in your component. InvokeAsync przyjmuje identyfikator funkcji języka JavaScript, która ma zostać wywołana wraz z dowolną liczbą argumentów do serializacji JSON.InvokeAsync takes an identifier for the JavaScript function that you wish to invoke along with any number of JSON-serializable arguments. Identyfikator funkcji jest względny w stosunku do zakresu globalnego ( window ).The function identifier is relative to the global scope (window). Jeśli chcesz wywołać window.someScope.someFunction , identyfikator to someScope.someFunction .If you wish to call window.someScope.someFunction, the identifier is someScope.someFunction. Nie ma potrzeby rejestrowania funkcji przed jej wywołaniem.There's no need to register the function before it's called. Zwracanym typem T musi być również kod JSON możliwy do serializacji.The return type T must also be JSON serializable. T powinien być zgodny z typem .NET, który najlepiej jest mapowany do zwracanego typu JSON.T should match the .NET type that best maps to the JSON type returned.

Funkcje języka JavaScript, które zwracają obietnicę , są wywoływane z InvokeAsync .JavaScript functions that return a Promise are called with InvokeAsync. InvokeAsync odpakuje obietnicę i zwraca wartość oczekiwaną przez obietnicę.InvokeAsync unwraps the Promise and returns the value awaited by the Promise.

W przypadku Blazor Server aplikacji z włączoną funkcją prerenderowania Wywoływanie kodu JavaScript nie jest możliwe podczas początkowego wstępnego renderowania.For Blazor Server apps with prerendering enabled, calling into JavaScript isn't possible during the initial prerendering. Wywołania międzyoperacyjne języka JavaScript muszą zostać odroczone do momentu ustanowienia połączenia z przeglądarką.JavaScript interop calls must be deferred until after the connection with the browser is established. Aby uzyskać więcej informacji, zobacz sekcję wykrywanie, gdy Blazor Server aplikacja jest prerenderowana .For more information, see the Detect when a Blazor Server app is prerendering section.

Poniższy przykład jest oparty na TextDecoder języku JavaScript.The following example is based on TextDecoder, a JavaScript-based decoder. W przykładzie pokazano, jak wywołać funkcję języka JavaScript z metody języka C#, która odciąża wymóg od kodu dewelopera do istniejącego interfejsu API języka JavaScript.The example demonstrates how to invoke a JavaScript function from a C# method that offloads a requirement from developer code to an existing JavaScript API. Funkcja JavaScript akceptuje tablicę bajtową z metody C#, dekoduje tablicę i zwraca tekst do składnika do wyświetlenia.The JavaScript function accepts a byte array from a C# method, decodes the array, and returns the text to the component for display.

Wewnątrz <head> elementu wwwroot/index.html ( Blazor WebAssembly ) lub Pages/_Host.cshtml ( Blazor Server ), podaj funkcję języka JavaScript, która używa TextDecoder do dekodowania przekazaną tablicę i zwracają zdekodowaną wartość:Inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server), provide a JavaScript function that uses TextDecoder to decode a passed array and return the decoded value:

<script>
  window.convertArray = (win1251Array) => {
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>

Kod JavaScript, taki jak kod przedstawiony w powyższym przykładzie, można również załadować z pliku JavaScript ( .js ) z odwołaniem do pliku skryptu:JavaScript code, such as the code shown in the preceding example, can also be loaded from a JavaScript file (.js) with a reference to the script file:

<script src="exampleJsInterop.js"></script>

Następujący składnik:The following component:

  • Wywołuje convertArray funkcję JavaScript przy użyciu JS przycisku składnika ( Convert Array ).Invokes the convertArray JavaScript function using JS when a component button (Convert Array) is selected.
  • Po wywołaniu funkcji języka JavaScript przenoszona tablica jest konwertowana na ciąg.After the JavaScript function is called, the passed array is converted into a string. Ciąg jest zwracany do składnika do wyświetlenia.The string is returned to the component for display.
@page "/call-js-example"
@inject IJSRuntime JS;

<h1>Call JavaScript Function Example</h1>

<button type="button" class="btn btn-primary" @onclick="ConvertArray">
    Convert Array
</button>

<p class="mt-2" style="font-size:1.6em">
    <span class="badge badge-success">
        @convertedText
    </span>
</p>

@code {
    // Quote (c)2005 Universal Pictures: Serenity
    // https://www.uphe.com/movies/serenity
    // David Krumholtz on IMDB: https://www.imdb.com/name/nm0472710/

    private MarkupString convertedText =
        new MarkupString("Select the <b>Convert Array</b> button.");

    private uint[] quoteArray = new uint[]
        {
            60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
            116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
            108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
            105, 118, 101, 114, 115, 101, 10, 10,
        };

    private async Task ConvertArray()
    {
        var text =
            await JS.InvokeAsync<string>("convertArray", quoteArray);

        convertedText = new MarkupString(text);
    }
}

IJSRuntimeIJSRuntime

Aby użyć IJSRuntime abstrakcji, należy zastosować jedną z następujących metod:To use the IJSRuntime abstraction, adopt any of the following approaches:

  • Wstrzyknąć IJSRuntime streszczenie do Razor składnika ( .razor ):Inject the IJSRuntime abstraction into the Razor component (.razor):

    @inject IJSRuntime JS
    
    @code {
        protected override void OnInitialized()
        {
            StocksService.OnStockTickerUpdated += stockUpdate =>
            {
                JS.InvokeVoidAsync("handleTickerChanged",
                    stockUpdate.symbol, stockUpdate.price);
            };
        }
    }
    

    W <head> elemencie wwwroot/index.html ( Blazor WebAssembly ) lub Pages/_Host.cshtml ( Blazor Server ), podaj handleTickerChanged funkcję języka JavaScript.Inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server), provide a handleTickerChanged JavaScript function. Funkcja jest wywoływana z JSRuntimeExtensions.InvokeVoidAsync i nie zwraca wartości:The function is called with JSRuntimeExtensions.InvokeVoidAsync and doesn't return a value:

    <script>
      window.handleTickerChanged = (symbol, price) => {
        // ... client-side processing/display code ...
      };
    </script>
    
  • Wstrzyknąć IJSRuntime streszczenie do klasy ( .cs ):Inject the IJSRuntime abstraction into a class (.cs):

    public class JsInteropClasses
    {
        private readonly IJSRuntime js;
    
        public JsInteropClasses(IJSRuntime js)
        {
            this.js = js;
        }
    
        public ValueTask<string> TickerChanged(string data)
        {
            return js.InvokeAsync<string>(
                "handleTickerChanged",
                stockUpdate.symbol,
                stockUpdate.price);
        }
    }
    

    W <head> elemencie wwwroot/index.html ( Blazor WebAssembly ) lub Pages/_Host.cshtml ( Blazor Server ), podaj handleTickerChanged funkcję języka JavaScript.Inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server), provide a handleTickerChanged JavaScript function. Funkcja jest wywoływana z JS.InvokeAsync i zwraca wartość:The function is called with JS.InvokeAsync and returns a value:

    <script>
      window.handleTickerChanged = (symbol, price) => {
        // ... client-side processing/display code ...
        return 'Done!';
      };
    </script>
    
  • W przypadku generowania zawartości dynamicznej przy użyciu BuildRenderTreenależy użyć [Inject] atrybutu:For dynamic content generation with BuildRenderTree, use the [Inject] attribute:

    [Inject]
    IJSRuntime JS { get; set; }
    

W aplikacji przykładowej po stronie klienta, która jest dołączona do tego tematu, dostępne są dwie funkcje języka JavaScript, które współdziałają z modelem DOM, aby odbierać dane wejściowe użytkownika i wyświetlać komunikat powitalny:In the client-side sample app that accompanies this topic, two JavaScript functions are available to the app that interact with the DOM to receive user input and display a welcome message:

  • showPrompt: Generuje monit o zaakceptowanie danych wprowadzonych przez użytkownika (nazwę użytkownika) i zwraca nazwę obiektu wywołującego.showPrompt: Produces a prompt to accept user input (the user's name) and returns the name to the caller.
  • displayWelcome: Przypisuje Komunikat powitalny od wywołującego do obiektu DOM z id welcome .displayWelcome: Assigns a welcome message from the caller to a DOM object with an id of welcome.

wwwroot/exampleJsInterop.js:wwwroot/exampleJsInterop.js:

window.exampleJsFunctions = {
  showPrompt: function (text) {
    return prompt(text, 'Type your name here');
  },
  displayWelcome: function (welcomeMessage) {
    document.getElementById('welcome').innerText = welcomeMessage;
  },
  returnArrayAsyncJs: function () {
    DotNet.invokeMethodAsync('BlazorWebAssemblySample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
      });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

Umieść <script> tag odwołujący się do pliku JavaScript w wwwroot/index.html pliku ( Blazor WebAssembly ) lub Pages/_Host.cshtml pliku ( Blazor Server ).Place the <script> tag that references the JavaScript file in the wwwroot/index.html file (Blazor WebAssembly) or Pages/_Host.cshtml file (Blazor Server).

wwwroot/index.html (Blazor WebAssembly):wwwroot/index.html (Blazor WebAssembly):

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Blazor WebAssembly Sample</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorWebAssemblySample.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="exampleJsInterop.js"></script>
</body>

</html>

Pages/_Host.cshtml (Blazor Server):Pages/_Host.cshtml (Blazor Server):

@page "/"
@namespace BlazorServerSample.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Blazor Server Sample</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorServerSample.styles.css" rel="stylesheet" />
</head>
<body>
    <component type="typeof(App)" render-mode="ServerPrerendered" />

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
    <script src="exampleJsInterop.js"></script>
</body>
</html>

Nie umieszczaj <script> znacznika w pliku składnika, ponieważ <script> nie można dynamicznie zaktualizować znacznika.Don't place a <script> tag in a component file because the <script> tag can't be updated dynamically.

Metody .NET współdziałają z funkcją JavaScript w exampleJsInterop.js pliku przez wywołanie IJSRuntime.InvokeAsync ..NET methods interop with the JavaScript functions in the exampleJsInterop.js file by calling IJSRuntime.InvokeAsync.

IJSRuntimeAbstrakcja jest asynchroniczna, aby umożliwić obsługę Blazor Server scenariuszy.The IJSRuntime abstraction is asynchronous to allow for Blazor Server scenarios. Jeśli aplikacja jest Blazor WebAssembly aplikacją i chcesz wywołać funkcję JavaScript synchronicznie, downcast do IJSInProcessRuntime wywołania i Invoke zamiast tego.If the app is a Blazor WebAssembly app and you want to invoke a JavaScript function synchronously, downcast to IJSInProcessRuntime and call Invoke instead. Zalecamy, aby większość bibliotek międzyoperacyjnych JS używała asynchronicznych interfejsów API, aby upewnić się, że biblioteki są dostępne we wszystkich scenariuszach.We recommend that most JS interop libraries use the async APIs to ensure that the libraries are available in all scenarios.

Uwaga

Aby włączyć izolację JavaScript w standardowych modułach języka JavaScript, zobacz sekcję Blazor izolacja kodu JavaScript i odwołania do obiektów .To enable JavaScript isolation in standard JavaScript modules, see the Blazor JavaScript isolation and object references section.

Przykładowa aplikacja zawiera składnik demonstrujący międzyoperacyjność JS.The sample app includes a component to demonstrate JS interop. Składnik:The component:

  • Odbiera dane wprowadzane przez użytkownika za pośrednictwem wiersza polecenia języka JavaScript.Receives user input via a JavaScript prompt.
  • Zwraca tekst do składnika do przetworzenia.Returns the text to the component for processing.
  • Wywołuje drugą funkcję języka JavaScript, która współdziała z modelem DOM, aby wyświetlić komunikat powitalny.Calls a second JavaScript function that interacts with the DOM to display a welcome message.

Pages/JsInterop.razor:Pages/JsInterop.razor:

@page "/JSInterop"
@using {APP ASSEMBLY}.JsInteropClasses
@inject IJSRuntime JS

<h1>JavaScript Interop</h1>

<h2>Invoke JavaScript functions from .NET methods</h2>

<button type="button" class="btn btn-primary" @onclick="TriggerJsPrompt">
    Trigger JavaScript Prompt
</button>

<h3 id="welcome" style="color:green;font-style:italic"></h3>

@code {
    public async Task TriggerJsPrompt()
    {
        var name = await JS.InvokeAsync<string>(
                "exampleJsFunctions.showPrompt",
                "What's your name?");

        await JS.InvokeVoidAsync(
                "exampleJsFunctions.displayWelcome",
                $"Hello {name}! Welcome to Blazor!");
    }
}

Symbol zastępczy {APP ASSEMBLY} to nazwa zestawu aplikacji aplikacji (na przykład BlazorSample ).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

  1. Gdy TriggerJsPrompt jest wykonywane, wybierając Trigger JavaScript Prompt przycisk składnika, showPrompt Funkcja JavaScript dostępna w wwwroot/exampleJsInterop.js pliku jest wywoływana.When TriggerJsPrompt is executed by selecting the component's Trigger JavaScript Prompt button, the JavaScript showPrompt function provided in the wwwroot/exampleJsInterop.js file is called.
  2. showPromptFunkcja akceptuje dane wejściowe użytkownika (nazwę użytkownika), które są kodowane w formacie HTML i zwracane do składnika.The showPrompt function accepts user input (the user's name), which is HTML-encoded and returned to the component. Składnik przechowuje nazwę użytkownika w zmiennej lokalnej, name .The component stores the user's name in a local variable, name.
  3. Ciąg przechowywany w programie name jest zawarty w komunikacie powitalnym, który jest przesyłany do funkcji języka JavaScript, displayWelcome która renderuje Komunikat powitalny do znacznika nagłówka.The string stored in name is incorporated into a welcome message, which is passed to a JavaScript function, displayWelcome, which renders the welcome message into a heading tag.

Wywoływanie funkcji języka JavaScript typu voidCall a void JavaScript function

Użyj JSRuntimeExtensions.InvokeVoidAsync dla następujących:Use JSRuntimeExtensions.InvokeVoidAsync for the following:

Wykryj, kiedy Blazor Server aplikacja jest renderowanaDetect when a Blazor Server app is prerendering

Gdy aplikacja serwera Blazor jest wstępnie renderowana, niektóre akcje, takie jak wywoływanie kodu JavaScript, nie są możliwe, ponieważ połączenie z przeglądarką nie zostało nawiązane.While a Blazor Server app is prerendering, certain actions, such as calling into JavaScript, aren't possible because a connection with the browser hasn't been established. Składniki mogą być konieczne w różny sposób, gdy są wstępnie renderowane.Components may need to render differently when prerendered.

Aby opóźnić wywołania międzyoperacyjne języka JavaScript do momentu ustanowienia połączenia z przeglądarką, można użyć zdarzenia cyklu życia składnika OnAfterRenderAsync.To delay JavaScript interop calls until after the connection with the browser is established, you can use the OnAfterRenderAsync component lifecycle event. To zdarzenie jest wywoływane tylko wtedy, gdy aplikacja jest w pełni renderowana, a połączenie z klientem zostanie nawiązane.This event is only called after the app is fully rendered and the client connection is established.

@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync(
                "setElementText", divElement, "Text after render");
        }
    }
}

W powyższym przykładowym kodzie Podaj setElementText funkcję JavaScript wewnątrz <head> elementu wwwroot/index.html (Blazor webassembly) lub Pages/_Host.cshtml (Blazor Server).For the preceding example code, provide a setElementText JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). Funkcja jest wywoływana z JSRuntimeExtensions.InvokeVoidAsync i nie zwraca wartości:The function is called with JSRuntimeExtensions.InvokeVoidAsync and doesn't return a value:

<script>
  window.setElementText = (element, text) => element.innerText = text;
</script>

Ostrzeżenie

Poprzedni przykład modyfikuje Document Object Model (DOM) bezpośrednio wyłącznie w celach demonstracyjnych.The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Nie zaleca się bezpośredniej modyfikacji modelu DOM przy użyciu języka JavaScript w większości scenariuszy, ponieważ kod JavaScript może zakłócać śledzenie zmian Blazor.Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

Poniższy składnik pokazuje, jak używać międzyoperacyjności JavaScript jako części logiki inicjalizacji składnika w sposób, który jest zgodny z renderowaniem.The following component demonstrates how to use JavaScript interop as part of a component's initialization logic in a way that's compatible with prerendering. Składnik pokazuje, że można wyzwolić aktualizację renderowania z wewnątrz OnAfterRenderAsync .The component shows that it's possible to trigger a rendering update from inside OnAfterRenderAsync. Deweloper musi unikać tworzenia pętli nieskończonej w tym scenariuszu.The developer must avoid creating an infinite loop in this scenario.

Gdzie JSRuntime.InvokeAsync jest wywoływana, ElementRef jest używana tylko w, OnAfterRenderAsync a nie w żadnej wcześniejszej metodzie cyklu życia, ponieważ nie istnieje element JavaScript do momentu renderowania składnika.Where JSRuntime.InvokeAsync is called, ElementRef is only used in OnAfterRenderAsync and not in any earlier lifecycle method because there's no JavaScript element until after the component is rendered.

StateHasChanged jest wywoływana, aby przetworzyć składnik z nowym stanem uzyskanym z wywołania międzyoperacyjności JavaScript (Aby uzyskać więcej informacji, zobacz BlazorRenderowanie składników ASP.NET Core ).StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call (for more information, see BlazorRenderowanie składników ASP.NET Core). Kod nie tworzy pętli nieskończonej, ponieważ StateHasChanged jest wywoływana tylko wtedy, gdy infoFromJs jest null .The code doesn't create an infinite loop because StateHasChanged is only called when infoFromJs is null.

@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(infoFromJs ?? "No value yet")</strong>
</p>

Set value via JS interop call:
<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string infoFromJs;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && infoFromJs == null)
        {
            infoFromJs = await JSRuntime.InvokeAsync<string>(
                "setElementText", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

W powyższym przykładowym kodzie Podaj setElementText funkcję JavaScript wewnątrz <head> elementu wwwroot/index.html (Blazor webassembly) lub Pages/_Host.cshtml (Blazor Server).For the preceding example code, provide a setElementText JavaScript function inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). Funkcja jest wywoływana z IJSRuntime.InvokeAsync i zwraca wartość:The function is called withIJSRuntime.InvokeAsync and returns a value:

<script>
  window.setElementText = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Ostrzeżenie

Poprzedni przykład modyfikuje Document Object Model (DOM) bezpośrednio wyłącznie w celach demonstracyjnych.The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Nie zaleca się bezpośredniej modyfikacji modelu DOM przy użyciu języka JavaScript w większości scenariuszy, ponieważ kod JavaScript może zakłócać śledzenie zmian Blazor.Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

Przechwyć odwołania do elementówCapture references to elements

Niektóre scenariusze międzyoperacyjności JS wymagają odwołań do elementów HTML.Some JS interop scenarios require references to HTML elements. Na przykład Biblioteka interfejsu użytkownika może wymagać odwołania do elementu dla inicjalizacji lub może być konieczne wywołanie interfejsów API, takich jak focus lub play .For example, a UI library may require an element reference for initialization, or you might need to call command-like APIs on an element, such as focus or play.

Przechwyć odwołania do elementów HTML w składniku, korzystając z następującej metody:Capture references to HTML elements in a component using the following approach:

  • Dodaj @ref atrybut do elementu HTML.Add an @ref attribute to the HTML element.
  • Zdefiniuj pole typu, ElementReference którego nazwa pasuje do wartości @ref atrybutu.Define a field of type ElementReference whose name matches the value of the @ref attribute.

Poniższy przykład pokazuje przechwytywanie odwołania do username <input> elementu:The following example shows capturing a reference to the username <input> element:

<input @ref="username" ... />

@code {
    ElementReference username;
}

Ostrzeżenie

Użyj odwołania do elementu, aby zmodyfikować zawartość pustego elementu, który nie współdziała z Blazor .Only use an element reference to mutate the contents of an empty element that doesn't interact with Blazor. Ten scenariusz jest przydatny, gdy interfejs API innej firmy dostarcza zawartość do elementu.This scenario is useful when a third-party API supplies content to the element. Ponieważ Blazor nie współdziała z elementem, nie ma możliwości konfliktu między Blazor reprezentacją elementu a modelem dom.Because Blazor doesn't interact with the element, there's no possibility of a conflict between Blazor's representation of the element and the DOM.

W poniższym przykładzie jest niebezpieczne do mutacji zawartości listy nieuporządkowanej ( ul ), ponieważ Blazor współdziała z modelem dom w celu wypełnienia elementów listy elementu ( <li> ):In the following example, it's dangerous to mutate the contents of the unordered list (ul) because Blazor interacts with the DOM to populate this element's list items (<li>):

<ul ref="MyList">
    @foreach (var item in Todos)
    {
        <li>@item.Text</li>
    }
</ul>

Jeśli element JS Interop przyniesie zawartość elementu MyList i Blazor podejmuje próbę zastosowania różnic do elementu, różnice nie będą zgodne z modelem dom.If JS interop mutates the contents of element MyList and Blazor attempts to apply diffs to the element, the diffs won't match the DOM.

ElementReferenceJest przenoszona przez kod JavaScript za pośrednictwem międzyoperacyjnego kodu js.An ElementReference is passed through to JavaScript code via JS interop. Kod JavaScript odbiera HTMLElement wystąpienie, które może być używane z normalnymi interfejsami API modelu DOM.The JavaScript code receives an HTMLElement instance, which it can use with normal DOM APIs. Na przykład poniższy kod definiuje metodę rozszerzenia .NET, która umożliwia wysłanie kliknięcia myszą do elementu:For example, the following code defines a .NET extension method that enables sending a mouse click to an element:

exampleJsInterop.js:exampleJsInterop.js:

window.interopFunctions = {
  clickElement : function (element) {
    element.click();
  }
}

Uwaga

Użyj FocusAsync w kodzie C#, aby skoncentrować się na elemencie, który jest wbudowanym Blazor strukturą i współpracuje z odwołaniami do elementów.Use FocusAsync in C# code to focus an element, which is built-into the Blazor framework and works with element references.

Aby wywołać funkcję języka JavaScript, która nie zwraca wartości, użyj JSRuntimeExtensions.InvokeVoidAsync .To call a JavaScript function that doesn't return a value, use JSRuntimeExtensions.InvokeVoidAsync. Poniższy kod wyzwala zdarzenie po stronie klienta Click , wywołując poprzednią funkcję JavaScript z przechwyconą ElementReference :The following code triggers a client-side Click event by calling the preceding JavaScript function with the captured ElementReference:

@inject IJSRuntime JS

<button @ref="exampleButton">Example Button</button>

<button @onclick="TriggerClick">
    Trigger button click on exampleButton
</button>

@code {
    private ElementReference exampleButton;

    public async Task TriggerClick()
    {
        await JS.InvokeVoidAsync(
            "interopFunctions.clickElement", exampleButton);
    }
}

Aby użyć metody rozszerzenia, Utwórz statyczną metodę rozszerzenia, która odbiera IJSRuntime wystąpienie:To use an extension method, create a static extension method that receives the IJSRuntime instance:

public static async Task TriggerClickEvent(this ElementReference elementRef, 
    IJSRuntime js)
{
    await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}

clickElementMetoda jest wywoływana bezpośrednio dla obiektu.The clickElement method is called directly on the object. W poniższym przykładzie przyjęto założenie, że TriggerClickEvent Metoda jest dostępna z JsInteropClasses przestrzeni nazw:The following example assumes that the TriggerClickEvent method is available from the JsInteropClasses namespace:

@inject IJSRuntime JS
@using JsInteropClasses

<button @ref="exampleButton" />

<button @onclick="TriggerClick">
    Trigger button click on exampleButton
</button>

@code {
    private ElementReference exampleButton;

    public async Task TriggerClick()
    {
        await exampleButton.TriggerClickEvent(JS);
    }
}

Ważne

exampleButtonZmienna jest wypełniana tylko po wyrenderowaniu składnika.The exampleButton variable is only populated after the component is rendered. W przypadku przekazanie niewypełnionego ElementReference kodu JavaScript kod JavaScript otrzymuje wartość null .If an unpopulated ElementReference is passed to JavaScript code, the JavaScript code receives a value of null. Aby manipulować odwołaniami do elementów po zakończeniu renderowania składnika, użyj OnAfterRenderAsync OnAfterRender metody lub cyklu życia składnika.To manipulate element references after the component has finished rendering use the OnAfterRenderAsync or OnAfterRender component lifecycle methods.

Podczas pracy z typami ogólnymi i zwracają wartość, użyj ValueTask<TResult> :When working with generic types and returning a value, use ValueTask<TResult>:

public static ValueTask<T> GenericMethod<T>(this ElementReference elementRef, 
    IJSRuntime js)
{
    return js.InvokeAsync<T>("exampleJsFunctions.doSomethingGeneric", elementRef);
}

GenericMethod jest wywoływana bezpośrednio na obiekcie z typem.GenericMethod is called directly on the object with a type. W poniższym przykładzie przyjęto założenie, że GenericMethod jest dostępny z JsInteropClasses przestrzeni nazw:The following example assumes that the GenericMethod is available from the JsInteropClasses namespace:

@inject IJSRuntime JS
@using JsInteropClasses

<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>

<p>
    returnValue: @returnValue
</p>

@code {
    private ElementReference username;
    private string returnValue;

    private async Task OnClickMethod()
    {
        returnValue = await username.GenericMethod<string>(JS);
    }
}

Elementy odniesienia między składnikamiReference elements across components

ElementReferenceNie można przekazywać między składnikami, ponieważ:An ElementReference can't be passed between components because:

Aby składnik nadrzędny mógł udostępnić odwołanie do elementu innym składnikom, składnik nadrzędny może:For a parent component to make an element reference available to other components, the parent component can:

  • Zezwalaj składnikom podrzędnym na rejestrowanie wywołań zwrotnych.Allow child components to register callbacks.
  • Wywołaj zarejestrowane wywołania zwrotne podczas OnAfterRender zdarzenia z odwołaniem do elementu.Invoke the registered callbacks during the OnAfterRender event with the passed element reference. Pośrednio takie podejście umożliwia składnikom podrzędnym współdziałanie z odwołaniem do elementu nadrzędnego.Indirectly, this approach allows child components to interact with the parent's element reference.

Poniższy Blazor WebAssembly przykład ilustruje podejście.The following Blazor WebAssembly example illustrates the approach.

W programie <head> z wwwroot/index.html :In the <head> of wwwroot/index.html:

<style>
    .red { color: red }
</style>

W programie <body> z wwwroot/index.html :In the <body> of wwwroot/index.html:

<script>
    function setElementClass(element, className) {
        /** @type {HTMLElement} **/
        var myElement = element;
        myElement.classList.add(className);
    }
</script>

Pages/Index.razor (składnik nadrzędny):Pages/Index.razor (parent component):

@page "/"

<h1 @ref="title">Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />

Pages/Index.razor.cs:Pages/Index.razor.cs:

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;

namespace {APP ASSEMBLY}.Pages
{
    public partial class Index : 
        ComponentBase, IObservable<ElementReference>, IDisposable
    {
        private bool disposing;
        private IList<IObserver<ElementReference>> subscriptions = 
            new List<IObserver<ElementReference>>();
        private ElementReference title;

        protected override void OnAfterRender(bool firstRender)
        {
            base.OnAfterRender(firstRender);

            foreach (var subscription in subscriptions)
            {
                try
                {
                    subscription.OnNext(title);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

        public void Dispose()
        {
            disposing = true;

            foreach (var subscription in subscriptions)
            {
                try
                {
                    subscription.OnCompleted();
                }
                catch (Exception)
                {
                }
            }

            subscriptions.Clear();
        }

        public IDisposable Subscribe(IObserver<ElementReference> observer)
        {
            if (disposing)
            {
                throw new InvalidOperationException("Parent being disposed");
            }

            subscriptions.Add(observer);

            return new Subscription(observer, this);
        }

        private class Subscription : IDisposable
        {
            public Subscription(IObserver<ElementReference> observer, Index self)
            {
                Observer = observer;
                Self = self;
            }

            public IObserver<ElementReference> Observer { get; }
            public Index Self { get; }

            public void Dispose()
            {
                Self.subscriptions.Remove(Observer);
            }
        }
    }
}

Symbol zastępczy {APP ASSEMBLY} to nazwa zestawu aplikacji aplikacji (na przykład BlazorSample ).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

Shared/SurveyPrompt.razor (składnik podrzędny):Shared/SurveyPrompt.razor (child component):

@inject IJSRuntime JS

<div class="alert alert-secondary mt-4" role="alert">
    <span class="oi oi-pencil mr-2" aria-hidden="true"></span>
    <strong>@Title</strong>

    <span class="text-nowrap">
        Please take our
        <a target="_blank" class="font-weight-bold" 
            href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
    </span>
    and tell us what you think.
</div>

@code {
    [Parameter]
    public string Title { get; set; }
}

Shared/SurveyPrompt.razor.cs:Shared/SurveyPrompt.razor.cs:

using System;
using Microsoft.AspNetCore.Components;

namespace {APP ASSEMBLY}.Shared
{
    public partial class SurveyPrompt : 
        ComponentBase, IObserver<ElementReference>, IDisposable
    {
        private IDisposable subscription = null;

        [Parameter]
        public IObservable<ElementReference> Parent { get; set; }

        protected override void OnParametersSet()
        {
            base.OnParametersSet();

            if (subscription != null)
            {
                subscription.Dispose();
            }

            subscription = Parent.Subscribe(this);
        }

        public void OnCompleted()
        {
            subscription = null;
        }

        public void OnError(Exception error)
        {
            subscription = null;
        }

        public void OnNext(ElementReference value)
        {
            JS.InvokeAsync<object>(
                "setElementClass", new object[] { value, "red" });
        }

        public void Dispose()
        {
            subscription?.Dispose();
        }
    }
}

Symbol zastępczy {APP ASSEMBLY} to nazwa zestawu aplikacji aplikacji (na przykład BlazorSample ).The placeholder {APP ASSEMBLY} is the app's app assembly name (for example, BlazorSample).

Zabezpieczenia wywołań międzyoperacyjnych w ramach funkcjonalności JSHarden JS interop calls

Usługa JS Interop może zakończyć się niepowodzeniem z powodu błędów sieci i powinna być traktowana jako niezawodna.JS interop may fail due to networking errors and should be treated as unreliable. Domyślnie Blazor Server aplikacja przeprowadzi czas wywołań międzyoperacyjnych js na serwerze po jednej minucie.By default, a Blazor Server app times out JS interop calls on the server after one minute. Jeśli aplikacja może tolerować bardziej agresywny limit czasu, należy ustawić limit czasu przy użyciu jednej z następujących metod:If an app can tolerate a more aggressive timeout, set the timeout using one of the following approaches:

  • Globalnie w programie Startup.ConfigureServices Określ limit czasu:Globally in Startup.ConfigureServices, specify the timeout:

    services.AddServerSideBlazor(
        options => options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds({SECONDS}));
    
  • Dla wywołania w kodzie składnika pojedyncze wywołanie może określać limit czasu:Per-invocation in component code, a single call can specify the timeout:

    var result = await JS.InvokeAsync<string>("MyJSOperation", 
        TimeSpan.FromSeconds({SECONDS}), new[] { "Arg1" });
    

Więcej informacji o wyczerpaniu zasobów znajduje się w temacie Wskazówki dotyczące łagodzenia zagrożeń dla ASP.NET Core Blazor Server .For more information on resource exhaustion, see Wskazówki dotyczące łagodzenia zagrożeń dla ASP.NET Core Blazor Server.

Udostępnianie kodu międzyoperacyjnego w bibliotece klasShare interop code in a class library

Kod międzyoperacyjny JS można uwzględnić w bibliotece klas, co umożliwia udostępnianie kodu w pakiecie NuGet.JS interop code can be included in a class library, which allows you to share the code in a NuGet package.

Biblioteka klas obsługuje Osadzanie zasobów JavaScript w skompilowanym zestawie.The class library handles embedding JavaScript resources in the built assembly. Pliki JavaScript są umieszczane w wwwroot folderze.The JavaScript files are placed in the wwwroot folder. Narzędzia te zajmują się osadzaniem zasobów podczas kompilowania biblioteki.The tooling takes care of embedding the resources when the library is built.

Skompilowany pakiet NuGet jest przywoływany w pliku projektu aplikacji w taki sam sposób, w jaki jest przywoływany każdy pakiet NuGet.The built NuGet package is referenced in the app's project file the same way that any NuGet package is referenced. Po przywróceniu pakietu kod aplikacji może być wywoływany w języku JavaScript, tak jakby był w języku C#.After the package is restored, app code can call into JavaScript as if it were C#.

Aby uzyskać więcej informacji, zobacz RazorBiblioteki klas składników ASP.NET Core.For more information, see RazorBiblioteki klas składników ASP.NET Core.

Unikaj cyklicznych odwołań do obiektówAvoid circular object references

Obiekty, które zawierają odwołania cykliczne, nie mogą być serializowane na kliencie dla obu:Objects that contain circular references can't be serialized on the client for either:

  • Wywołania metody .NET..NET method calls.
  • Wywołania metody JavaScript z języka C#, gdy typem zwracanym są odwołania cykliczne.JavaScript method calls from C# when the return type has circular references.

Aby uzyskać więcej informacji, zobacz odwołania cykliczne nie są obsługiwane, zrób dwa (dotnet/aspnetcore #20525).For more information, see Circular references are not supported, take two (dotnet/aspnetcore #20525).

Blazor Izolacja kodu JavaScript i odwołania do obiektówBlazor JavaScript isolation and object references

Blazor Włącza izolację JavaScript w standardowych modułach języka JavaScript.Blazor enables JavaScript isolation in standard JavaScript modules. Izolacja JavaScript zapewnia następujące korzyści:JavaScript isolation provides the following benefits:

  • Zaimportowana wartość JavaScript nie jest już zanieczyszczana globalną przestrzenią nazw.Imported JavaScript no longer pollutes the global namespace.
  • Odbiorcy biblioteki i składników nie są zobowiązani do zaimportowania powiązanego języka JavaScript.Consumers of a library and components aren't required to import the related JavaScript.

Na przykład poniższy moduł JavaScript eksportuje funkcję JavaScript do wyświetlania monitu przeglądarki:For example, the following JavaScript module exports a JavaScript function for showing a browser prompt:

export function showPrompt(message) {
  return prompt(message, 'Type anything here');
}

Dodaj poprzedni moduł JavaScript do biblioteki .NET jako statyczny element zawartości sieci Web ( wwwroot/exampleJsInterop.js ), a następnie zaimportuj moduł do kodu platformy .NET przy użyciu IJSRuntime usługi.Add the preceding JavaScript module to a .NET library as a static web asset (wwwroot/exampleJsInterop.js) and then import the module into the .NET code using the IJSRuntime service. Usługa jest wstrzykiwana jako js (niepokazywana) dla następującego przykładu:The service is injected as js (not shown) for the following example:

var module = await js.InvokeAsync<IJSObjectReference>(
    "import", "./_content/MyComponents/exampleJsInterop.js");

importIdentyfikator w poprzednim przykładzie jest specjalnym identyfikatorem używanym specjalnie do importowania modułu JavaScript.The import identifier in the preceding example is a special identifier used specifically for importing a JavaScript module. Określ moduł przy użyciu jego stabilnej statycznej ścieżki zasobów sieci Web: ./_content/{LIBRARY NAME}/{PATH UNDER WWWROOT} .Specify the module using its stable static web asset path: ./_content/{LIBRARY NAME}/{PATH UNDER WWWROOT}. Segment ścieżki dla bieżącego katalogu ( ./ ) jest wymagany, aby można było utworzyć poprawną statyczną ścieżkę zasobu do pliku JavaScript.The path segment for the current directory (./) is required in order to create the correct static asset path to the JavaScript file. Symbol zastępczy {LIBRARY NAME} jest nazwą biblioteki.The placeholder {LIBRARY NAME} is the library name. Symbol zastępczy {PATH UNDER WWWROOT} jest ścieżką do skryptu w sekcji wwwroot .The placeholder {PATH UNDER WWWROOT} is the path to the script under wwwroot.

IJSRuntime Importuje moduł jako IJSObjectReference , który reprezentuje odwołanie do obiektu JavaScript z kodu platformy .NET.IJSRuntime imports the module as a IJSObjectReference, which represents a reference to a JavaScript object from .NET code. Użyj IJSObjectReference do wywołania wyeksportowanych funkcji języka JavaScript z modułu:Use the IJSObjectReference to invoke exported JavaScript functions from the module:

public async ValueTask<string> Prompt(string message)
{
    return await module.InvokeAsync<string>("showPrompt", message);
}

IJSInProcessObjectReference reprezentuje odwołanie do obiektu JavaScript, którego funkcje mogą być wywoływane synchronicznie.IJSInProcessObjectReference represents a reference to a JavaScript object whose functions can be invoked synchronously.

Korzystanie z bibliotek języka JavaScript, które renderują interfejs użytkownika (elementy DOM)Use of JavaScript libraries that render UI (DOM elements)

Czasami warto używać bibliotek języka JavaScript, które tworzą widoczne elementy interfejsu użytkownika w modelu DOM przeglądarki.Sometimes you may wish to use JavaScript libraries that produce visible user interface elements within the browser DOM. Na pierwszy rzut oka może wydawać się trudne, ponieważ Blazor system różnicowania polega na kontroli nad drzewem elementów Dom i uruchamianiu do błędów, jeśli jakiś kod zewnętrzny przydzieli drzewo Dom i unieważnia swój mechanizm do zastosowania różnic.At first glance, this might seem difficult because Blazor's diffing system relies on having control over the tree of DOM elements and runs into errors if some external code mutates the DOM tree and invalidates its mechanism for applying diffs. Nie jest to Blazor specyficzne ograniczenie.This isn't a Blazor-specific limitation. To samo wyzwanie występuje w przypadku wszystkich opartych na różnicach struktur interfejsu użytkownika.The same challenge occurs with any diff-based UI framework.

Na szczęście proste osadzanie wygenerowanego zewnętrznie interfejsu użytkownika w Blazor interfejsie użytkownika składnika jest niezawodne.Fortunately, it's straightforward to embed externally-generated UI within a Blazor component UI reliably. Zalecaną techniką jest .razor utworzenie pustego elementu przez kod składnika (plik).The recommended technique is to have the component's code (.razor file) produce an empty element. W odniesieniu do Blazor systemu różnicowego, element jest zawsze pusty, dlatego moduł renderowania nie odnosi się do elementu i zamiast niego opuszcza jego zawartość.As far as Blazor's diffing system is concerned, the element is always empty, so the renderer does not recurse into the element and instead leaves its contents alone. Dzięki temu można bezpiecznie wypełnić element dowolną zewnętrznie zarządzaną zawartością.This makes it safe to populate the element with arbitrary externally-managed content.

Poniższy przykład demonstruje koncepcję.The following example demonstrates the concept. W if instrukcji when firstRender true , zrób coś z myElement .Within the if statement when firstRender is true, do something with myElement. Na przykład Wywołaj zewnętrzną bibliotekę JavaScript w celu jej wypełnienia.For example, call an external JavaScript library to populate it. Blazor pozostawia zawartość elementu tylko do momentu usunięcia tego składnika.Blazor leaves the element's contents alone until this component itself is removed. Po usunięciu składnika zostanie również usunięta cała poddrzewo DOM składnika.When the component is removed, the component's entire DOM subtree is also removed.

<h1>Hello! This is a Blazor component rendered at @DateTime.Now</h1>

<div @ref="myElement"></div>

@code {
    HtmlElement myElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            ...
        }
    }
}

Jako bardziej szczegółowy przykład rozważmy poniższy składnik, który renderuje interaktywną mapę przy użyciu interfejsów API MapBox typu open source:As a more detailed example, consider the following component that renders an interactive map using the open-source Mapbox APIs:

@inject IJSRuntime JS
@implements IAsyncDisposable

<div @ref="mapElement" style='width: 400px; height: 300px;'></div>

<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>

@code
{
    ElementReference mapElement;
    IJSObjectReference mapModule;
    IJSObjectReference mapInstance;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            mapModule = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./mapComponent.js");
            mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
                "addMapToElement", mapElement);
        }
    }

    Task ShowAsync(double latitude, double longitude)
        => mapModule.InvokeVoidAsync("setMapCenter", mapInstance, latitude, 
            longitude).AsTask();

    private async ValueTask IAsyncDisposable.DisposeAsync()
    {
        await mapInstance.DisposeAsync();
        await mapModule.DisposeAsync();
    }
}

Odpowiedni moduł JavaScript, który powinien być umieszczony w wwwroot/mapComponent.js , jest następujący:The corresponding JavaScript module, which should be placed at wwwroot/mapComponent.js, is as follows:

import 'https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js';

// TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM 
// https://account.mapbox.com
mapboxgl.accessToken = '{ACCESS TOKEN}';

export function addMapToElement(element) {
  return new mapboxgl.Map({
    container: element,
    style: 'mapbox://styles/mapbox/streets-v11',
    center: [-74.5, 40],
    zoom: 9
  });
}

export function setMapCenter(map, latitude, longitude) {
  map.setCenter([longitude, latitude]);
}

W poprzednim przykładzie Zastąp ciąg {ACCESS TOKEN} prawidłowym tokenem dostępu, z którego można uzyskać dostęp https://account.mapbox.com .In the preceding example, replace the string {ACCESS TOKEN} with a valid access token that you can get from https://account.mapbox.com.

Aby wygenerować poprawne style, Dodaj następujący tag arkusza stylów do strony HTML hosta ( index.html lub _Host.cshtml ):To produce correct styling, add the following stylesheet tag to the host HTML page (index.html or _Host.cshtml):

<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" />

W powyższym przykładzie jest tworzony interaktywny interfejs użytkownika mapy, w którym użytkownik:The preceding example produces an interactive map UI, in which the user:

  • Można przeciągnąć, aby przewinąć lub powiększyć.Can drag to scroll or zoom.
  • Kliknij przycisk przyciski, aby przejść do wstępnie zdefiniowanych lokalizacji.Click buttons to jump to predefined locations.

Mapa MapBox ulica Tokio, Japonia z przyciskami do wyboru Bristol, Zjednoczone Królestwo i Tokio, Japonia

Najważniejsze kwestie, które należy zrozumieć:The key points to understand are:

  • Wartość <div> with @ref="mapElement" ma wartość pustą Blazor .The <div> with @ref="mapElement" is left empty as far as Blazor is concerned. Z tego względu mapbox-gl.js można bezpiecznie wypełnić i zmodyfikować jego zawartość w czasie.It's therefore safe for mapbox-gl.js to populate it and modify its contents over time. Tej techniki można użyć z dowolną biblioteką języka JavaScript, która renderuje interfejs użytkownika.You can use this technique with any JavaScript library that renders UI. Można nawet osadzić składniki z struktury SPA w języku JavaScript innej firmy w Blazor składnikach programu, o ile nie spróbują się dowiedzieć i modyfikować innych części strony.You could even embed components from a third-party JavaScript SPA framework inside Blazor components, as long as they don't try to reach out and modify other parts of the page. Nie jest bezpieczny dla zewnętrznego kodu JavaScript, aby modyfikować elementy, które nie są Blazor traktowane jako puste.It is not safe for external JavaScript code to modify elements that Blazor does not regard as empty.
  • Korzystając z tego podejścia, należy wziąć pod uwagę reguły dotyczące sposobu Blazor zachowywania lub niszczenia elementów dom.When using this approach, bear in mind the rules about how Blazor retains or destroys DOM elements. W poprzednim przykładzie składnik bezpiecznie obsługuje przycisk kliknij zdarzenia i aktualizuje istniejące wystąpienie mapy, ponieważ elementy DOM są zachowywane w miarę możliwości domyślnie.In the preceding example, the component safely handles button click events and updates the existing map instance because DOM elements are retained where possible by default. Jeśli renderuje listę elementów mapy z wewnątrz @foreach pętli, chcesz użyć, @key Aby zapewnić zachowywanie wystąpień składników.If you were rendering a list of map elements from inside a @foreach loop, you want to use @key to ensure the preservation of component instances. W przeciwnym razie zmiany w danych listy mogą spowodować wystąpienie składników, aby zachować stan poprzednich wystąpień w niepożądany sposób.Otherwise, changes in the list data could cause component instances to retain the state of previous instances in an undesirable manner. Aby uzyskać więcej informacji, zobacz Używanie @key do zachowywania elementów i składników.For more information, see using @key to preserve elements and components.

Ponadto w poprzednim przykładzie pokazano, jak można hermetyzować logikę JavaScript i zależności w module ES6 i ładować ją dynamicznie przy użyciu import identyfikatora.Additionally, the preceding example shows how it's possible to encapsulate JavaScript logic and dependencies within an ES6 module and load it dynamically using the import identifier. Aby uzyskać więcej informacji, zobacz temat izolacja JavaScript i odwołania do obiektów.For more information, see JavaScript isolation and object references.

Limity rozmiaru dla wywołań międzyoperacyjnych w JSSize limits on JS interop calls

W programie Blazor WebAssembly platforma nie nakłada limitu rozmiaru danych wejściowych i wyjściowych w programie js.In Blazor WebAssembly, the framework doesn't impose a limit on the size of JS interop inputs and outputs.

W programie Blazor Server wywołania programu js Interop są ograniczone przez maksymalny SignalR rozmiar przychodzących komunikatów dozwolony dla metod koncentratora, które są wymuszane przez HubOptions.MaximumReceiveMessageSize (domyślnie: 32 KB).In Blazor Server, JS interop calls are limited in size by the maximum incoming SignalR message size permitted for hub methods, which is enforced by HubOptions.MaximumReceiveMessageSize (default: 32 KB). Program JS do SignalR komunikatów programu .NET większych niż MaximumReceiveMessageSize Zgłoś błąd.JS to .NET SignalR messages larger than MaximumReceiveMessageSize throw an error. Platforma nie nakłada limitu rozmiaru SignalR komunikatu z centrum na klienta.The framework doesn't impose a limit on the size of a SignalR message from the hub to a client. Aby uzyskać więcej informacji, zobacz Wywoływanie metod .NET z funkcji języka JavaScript w ASP.NET Core Blazor.For more information, see Wywoływanie metod .NET z funkcji języka JavaScript w ASP.NET Core Blazor.

Moduły JSJS modules

W przypadku izolacji JS program JS współdziała z domyślną obsługą języka ECMAScript ( model ECMAScript) w przeglądarce.For JS isolation, JS interop works with the browser's default support for EcmaScript modules (ESM) (ECMAScript specification).

Nieskierowany międzyoperacyjna JSUnmarshalled JS interop

Blazor WebAssembly składniki mogą mieć niską wydajność, gdy obiekty .NET są serializowane pod kątem współdziałania z JS i jeden z następujących warunków jest spełniony:Blazor WebAssembly components may experience poor performance when .NET objects are serialized for JS interop and either of the following are true:

  • Duże ilości obiektów .NET są szybko serializowane.A high volume of .NET objects are rapidly serialized. Przykład: wywołania międzyoperacyjnego JS są wykonywane na podstawie przeniesienia urządzenia wejściowego, takiego jak wirowanie kółka myszy.Example: JS interop calls are made on the basis of moving an input device, such as spinning a mouse wheel.
  • Duże obiekty .NET lub wiele obiektów .NET muszą być serializowane dla międzyoperacyjnego JS.Large .NET objects or many .NET objects must be serialized for JS interop. Przykład: wywołania międzyoperacyjne JS wymagają serializowania dziesiątek plików.Example: JS interop calls require serializing dozens of files.

IJSUnmarshalledObjectReference reprezentuje odwołanie do obiektu JavaScript, którego funkcje mogą być wywoływane bez nakładu na Serializowanie danych platformy .NET.IJSUnmarshalledObjectReference represents a reference to an JavaScript object whose functions can be invoked without the overhead of serializing .NET data.

W poniższym przykładzie:In the following example:

  • Struktura zawierająca ciąg i liczbę całkowitą są przesyłane do kodu JavaScript.A struct containing a string and an integer is passed unserialized to JavaScript.
  • Funkcje języka JavaScript przetwarzają dane i zwracają wartość logiczną lub ciąg do obiektu wywołującego.JavaScript functions process the data and return either a boolean or string to the caller.
  • Ciąg JavaScript nie jest bezpośrednio konwertowany do obiektu platformy .NET string .A JavaScript string isn't directly convertible into a .NET string object. unmarshalledFunctionReturnStringFunkcja wywołuje funkcję BINDING.js_string_to_mono_string do zarządzania konwersją ciągu JavaScript.The unmarshalledFunctionReturnString function calls BINDING.js_string_to_mono_string to manage the conversion of a Javascript string.

Uwaga

Poniższe przykłady nie są typowymi przypadkami użycia w tym scenariuszu, ponieważ Struktura przeniesiona do języka JavaScript nie powoduje niskiej wydajności składników.The following examples aren't typical use cases for this scenario because the struct passed to JavaScript doesn't result in poor component performance. W przykładzie jest wykorzystywany mały obiekt tylko w celu przedstawienia koncepcji przekazywania nieserializowanych danych platformy .NET.The example uses a small object merely to demonstrate the concepts for passing unserialized .NET data.

Zawartość <script> bloku w wwwroot/index.html lub zewnętrznym pliku JavaScript, do którego odwołuje się wwwroot/index.html :Content of a <script> block in wwwroot/index.html or an external Javascript file referenced by wwwroot/index.html:

window.returnJSObjectReference = () => {
    return {
        unmarshalledFunctionReturnBoolean: function (fields) {
            const name = Blazor.platform.readStringField(fields, 0);
            const year = Blazor.platform.readInt32Field(fields, 8);

            return name === "Brigadier Alistair Gordon Lethbridge-Stewart" &&
                year === 1968;
        },
        unmarshalledFunctionReturnString: function (fields) {
            const name = Blazor.platform.readStringField(fields, 0);
            const year = Blazor.platform.readInt32Field(fields, 8);

            return BINDING.js_string_to_mono_string(`Hello, ${name} (${year})!`);
        }
    };
}

Ostrzeżenie

js_string_to_mono_stringNazwa funkcji, zachowanie i istnienie mogą ulec zmianie w przyszłej wersji platformy .NET.The js_string_to_mono_string function name, behavior, and existence is subject to change in a future release of .NET. Na przykład:For example:

  • Prawdopodobnie zmieniono nazwę funkcji.The function is likely to be renamed.
  • Sama funkcja może zostać usunięta na korzyść automatycznej konwersji ciągów przez strukturę.The function itself might be removed in favor of automatic conversion of strings by the framework.

Pages/UnmarshalledJSInterop.razor (URL: /unmarshalled-js-interop ):Pages/UnmarshalledJSInterop.razor (URL: /unmarshalled-js-interop):

@page "/unmarshalled-js-interop"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS

<h1>Unmarshalled JS interop</h1>

@if (callResultForBoolean)
{
    <p>JS interop was successful!</p>
}

@if (!string.IsNullOrEmpty(callResultForString))
{
    <p>@callResultForString</p>
}

<p>
    <button @onclick="CallJSUnmarshalledForBoolean">
        Call Unmarshalled JS & Return Boolean
    </button>
    <button @onclick="CallJSUnmarshalledForString">
        Call Unmarshalled JS & Return String
    </button>
</p>

<p>
    <a href="https://www.doctorwho.tv">Doctor Who</a>
    is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>

@code {
    private bool callResultForBoolean;
    private string callResultForString;

    private void CallJSUnmarshalledForBoolean()
    {
        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;

        var jsUnmarshalledReference = unmarshalledRuntime
            .InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
                "returnJSObjectReference");

        callResultForBoolean = 
            jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
                "unmarshalledFunctionReturnBoolean", GetStruct());
    }

    private void CallJSUnmarshalledForString()
    {
        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;

        var jsUnmarshalledReference = unmarshalledRuntime
            .InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
                "returnJSObjectReference");

        callResultForString = 
            jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, string>(
                "unmarshalledFunctionReturnString", GetStruct());
    }

    private InteropStruct GetStruct()
    {
        return new InteropStruct
        {
            Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
            Year = 1968,
        };
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InteropStruct
    {
        [FieldOffset(0)]
        public string Name;

        [FieldOffset(8)]
        public int Year;
    }
}

Jeśli IJSUnmarshalledObjectReference wystąpienie nie zostanie usunięte w kodzie C#, można je usunąć w języku JavaScript.If an IJSUnmarshalledObjectReference instance isn't disposed in C# code, it can be disposed in JavaScript. Następująca dispose Funkcja usuwa odwołanie do obiektu w przypadku wywołania z JavaScript:The following dispose function disposes the object reference when called from JavaScript:

window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
    return {
        dispose: function () {
            DotNet.disposeJSObjectReference(this);
        },

        ...
    };
}

Typy tablic mogą być konwertowane z obiektów JavaScript do obiektów .NET przy użyciu js_typed_array_to_array , ale tablica JavaScript musi być tablicą z określonym typem.Array types can be converted from JavaScript objects into .NET objects using js_typed_array_to_array, but the JavaScript array must be a typed array. Tablice z JavaScript mogą być odczytywane w kodzie C# jako tablica obiektów .NET ( object[] ).Arrays from JavaScript can be read in C# code as a .NET object array (object[]).

Inne typy danych, takie jak tablice ciągów, można przekonwertować, ale wymagają utworzenia nowego obiektu tablicy mono ( mono_obj_array_new ) i ustawienia jego wartości ( mono_obj_array_set ).Other data types, such as string arrays, can be converted but require creating a new Mono array object (mono_obj_array_new) and setting its value (mono_obj_array_set).

Ostrzeżenie

Funkcje języka JavaScript udostępniane przez Blazor platformę, takie jak js_typed_array_to_array , mono_obj_array_new i mono_obj_array_set , podlegają zmianom nazw, zmianom behawioralnym lub usunięciu w przyszłych wersjach platformy .NET.JavaScript functions provided by the Blazor framework, such as js_typed_array_to_array, mono_obj_array_new, and mono_obj_array_set, are subject to name changes, behavioral changes, or removal in future releases of .NET.

Dodatkowe zasobyAdditional resources