ASP.NET Core Blazor взаимодействия JavaScriptASP.NET Core Blazor JavaScript interop

Хавьер Калварро Воронков, Даниэль Roth)и Люк ЛаСаМBy Javier Calvarro Nelson, Daniel Roth, and Luke Latham

Важно!

Blazor WebAssembly (предварительная версия)Blazor WebAssembly in preview

Blazor Server поддерживается только в ASP.NET Core 3.0 и последующих версиях.Blazor Server is supported in ASP.NET Core 3.0. Blazor WebAssembly предоставляется как предварительная версия для ASP.NET Core 3.1.Blazor WebAssembly is in preview for ASP.NET Core 3.1.

Blazor приложение может вызывать функции JavaScript из кода JavaScript в методах .NET и .NET.A Blazor app can invoke JavaScript functions from .NET and .NET methods from JavaScript code.

Просмотреть или скачать образец кода (как скачивать)View or download sample code (how to download)

Вызов функций JavaScript из методов .NETInvoke JavaScript functions from .NET methods

В некоторых случаях для вызова функции JavaScript требуется код .NET.There are times when .NET code is required to call a JavaScript function. Например, вызов JavaScript может предоставлять приложению возможности браузера или функциональные возможности из библиотеки JavaScript.For example, a JavaScript call can expose browser capabilities or functionality from a JavaScript library to the app. Этот сценарий называется совместимостью JavaScript (JS Interop).This scenario is called JavaScript interoperability (JS interop).

Чтобы вызвать JavaScript из .NET, используйте абстракцию IJSRuntime.To call into JavaScript from .NET, use the IJSRuntime abstraction. Метод InvokeAsync<T> принимает идентификатор для функции JavaScript, которую нужно вызвать вместе с любым числом аргументов, сериализуемых в формат JSON.The InvokeAsync<T> method takes an identifier for the JavaScript function that you wish to invoke along with any number of JSON-serializable arguments. Идентификатор функции задается относительно глобальной области (window).The function identifier is relative to the global scope (window). Если вы хотите вызвать window.someScope.someFunction, идентификатор будет someScope.someFunction.If you wish to call window.someScope.someFunction, the identifier is someScope.someFunction. Нет необходимости регистрировать функцию перед ее вызовом.There's no need to register the function before it's called. Тип возвращаемого значения T также должен быть сериализуемым в формат JSON.The return type T must also be JSON serializable. T должен соответствовать типу .NET, который лучше сопоставить с возвращаемым типом JSON.T should match the .NET type that best maps to the JSON type returned.

Для приложений Blazor Server:For Blazor Server apps:

  • Приложение Blazor Server обрабатывает несколько запросов пользователей.Multiple user requests are processed by the Blazor Server app. Не вызывайте JSRuntime.Current в компоненте для вызова функций JavaScript.Don't call JSRuntime.Current in a component to invoke JavaScript functions.
  • Внедрить абстракцию IJSRuntime и использовать внедренный объект для вызова взаимодействия JS.Inject the IJSRuntime abstraction and use the injected object to issue JS interop calls.
  • При предварительном отображении Blazor приложения вызов JavaScript невозможен, поскольку не было установлено соединение с браузером.While a Blazor app is prerendering, calling into JavaScript isn't possible because a connection with the browser hasn't been established. Дополнительные сведения см. в разделе Обнаружение времени подготовки Blazor приложения .For more information, see the Detect when a Blazor app is prerendering section.

Следующий пример основан на текстдекодер, экспериментальном декодере на основе JavaScript.The following example is based on TextDecoder, an experimental JavaScript-based decoder. В примере показано, как вызвать функцию JavaScript из C# метода.The example demonstrates how to invoke a JavaScript function from a C# method. Функция JavaScript принимает массив байтов из C# метода, декодирует массив и возвращает текст компоненту для вывода.The JavaScript function accepts a byte array from a C# method, decodes the array, and returns the text to the component for display.

В <head> элементе wwwroot/index.HTML (Blazor Assembly) или pages/_Host. cshtml (Blazor Server) Укажите функцию JavaScript, которая использует TextDecoder для декодирования переданного массива и возврата декодированного значения: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>

Код JavaScript, например код, показанный в предыдущем примере, можно также загрузить из файла JavaScript ( . js) со ссылкой на файл скрипта: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>

Следующий компонент:The following component:

  • Вызывает функцию JavaScript convertArray, используя JSRuntime при выборе кнопки компонента (преобразование массива).Invokes the convertArray JavaScript function using JSRuntime when a component button (Convert Array) is selected.
  • После вызова функции JavaScript переданный массив преобразуется в строку.After the JavaScript function is called, the passed array is converted into a string. Строка возвращается компоненту для вывода.The string is returned to the component for display.
@page "/call-js-example"
@inject IJSRuntime JSRuntime;

<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 JSRuntime.InvokeAsync<string>("convertArray", QuoteArray);

        ConvertedText = new MarkupString(text);

        StateHasChanged();
    }
}

Использование ИжсрунтимеUse of IJSRuntime

Чтобы использовать абстракцию IJSRuntime, следует принять любой из следующих подходов:To use the IJSRuntime abstraction, adopt any of the following approaches:

  • Внедрить IJSRuntimeую абстракцию в компонент Razor ( . Razor):Inject the IJSRuntime abstraction into the Razor component (.razor):

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

    В элементе <head> wwwroot/index.HTML (Blazor сборки) или pages/_Host. cshtml (Blazor Server) укажите handleTickerChanged функцию JavaScript.Inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server), provide a handleTickerChanged JavaScript function. Функция вызывается с IJSRuntime.InvokeVoidAsync и не возвращает значение:The function is called with IJSRuntime.InvokeVoidAsync and doesn't return a value:

    <script>
      window.handleTickerChanged = (symbol, price) => {
        // ... client-side processing/display code ...
      };
    </script>
    
  • Внедрить IJSRuntimeую абстракцию в класс (CS):Inject the IJSRuntime abstraction into a class (.cs):

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

    В элементе <head> wwwroot/index.HTML (Blazor сборки) или pages/_Host. cshtml (Blazor Server) укажите handleTickerChanged функцию JavaScript.Inside the <head> element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server), provide a handleTickerChanged JavaScript function. Функция вызывается с JSRuntime.InvokeAsync и возвращает значение:The function is called with JSRuntime.InvokeAsync and returns a value:

    <script>
      window.handleTickerChanged = (symbol, price) => {
        // ... client-side processing/display code ...
        return 'Done!';
      };
    </script>
    
  • Для динамического создания содержимого с помощью буилдрендертрииспользуйте атрибут [Inject]:For dynamic content generation with BuildRenderTree, use the [Inject] attribute:

    [Inject]
    IJSRuntime JSRuntime { get; set; }
    

В примере приложения на стороне клиента, прилагаемом к этой теме, для приложения, которое взаимодействует с моделью DOM для получения вводимых пользователем данных и вывода приветственного сообщения, доступны две функции JavaScript.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 – выдает запрос на ввод пользователя (имя пользователя) и возвращает имя вызывающему объекту.showPrompt – Produces a prompt to accept user input (the user's name) and returns the name to the caller.
  • displayWelcome – назначает приветственное сообщение объекту модели DOM с помощью id welcome.displayWelcome – Assigns a welcome message from the caller to a DOM object with an id of welcome.

wwwroot/ексамплежсинтероп. 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('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
    });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

Поместите тег <script>, который ссылается на файл JavaScript, в файл wwwroot/index.HTML (Blazor сборки) или pages/_Host. cshtml (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ная сборка):wwwroot/index.html (Blazor WebAssembly):

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Blazor WebAssembly Sample</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/site.css" rel="stylesheet" />
</head>

<body>
    <app>Loading...</app>

    <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 BlazorSample.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" />
</head>
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <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>

Не размещайте тег <script> в файле компонента, так как тег <script> нельзя обновлять динамически.Don't place a <script> tag in a component file because the <script> tag can't be updated dynamically.

Методы .NET взаимодействует с функциями JavaScript в файле ексамплежсинтероп. js путем вызова IJSRuntime.InvokeAsync<T>..NET methods interop with the JavaScript functions in the exampleJsInterop.js file by calling IJSRuntime.InvokeAsync<T>.

Абстракция IJSRuntime является асинхронной, что позволяет выполнять сценарии с Blazor Server.The IJSRuntime abstraction is asynchronous to allow for Blazor Server scenarios. Если приложение является Blazor приложением сборки и необходимо синхронно вызвать функцию JavaScript, произведение производных IJSInProcessRuntime и вызвать Invoke<T>.If the app is a Blazor WebAssembly app and you want to invoke a JavaScript function synchronously, downcast to IJSInProcessRuntime and call Invoke<T> instead. Рекомендуется, чтобы большинство библиотек взаимодействия JS использовали асинхронные интерфейсы API, чтобы обеспечить доступность библиотек во всех сценариях.We recommend that most JS interop libraries use the async APIs to ensure that the libraries are available in all scenarios.

Пример приложения включает компонент для демонстрации взаимодействия с JS.The sample app includes a component to demonstrate JS interop. Компонент:The component:

  • Получает входные данные пользователя с помощью командной строки JavaScript.Receives user input via a JavaScript prompt.
  • Возвращает текст компонента для обработки.Returns the text to the component for processing.
  • Вызывает вторую функцию JavaScript, которая взаимодействует с моделью DOM для вывода приветственного сообщения.Calls a second JavaScript function that interacts with the DOM to display a welcome message.

Pages/жсинтероп. Razor:Pages/JSInterop.razor:

@page "/JSInterop"
@using BlazorSample.JsInteropClasses
@inject IJSRuntime JSRuntime

<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()
    {
        // showPrompt is implemented in wwwroot/exampleJsInterop.js
        var name = await JSRuntime.InvokeAsync<string>(
                "exampleJsFunctions.showPrompt",
                "What's your name?");
        // displayWelcome is implemented in wwwroot/exampleJsInterop.js
        await JSRuntime.InvokeVoidAsync(
                "exampleJsFunctions.displayWelcome",
                $"Hello {name}! Welcome to Blazor!");
    }
}
  1. Когда TriggerJsPrompt выполняется при нажатии кнопки запроса JavaScript триггера компонента, вызывается функция JavaScript showPrompt, предоставленная в файле wwwroot/ексамплежсинтероп. js .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. Функция showPrompt принимает ввод пользователя (имя пользователя), который кодируется в формате HTML и возвращается компоненту.The showPrompt function accepts user input (the user's name), which is HTML-encoded and returned to the component. Компонент сохраняет имя пользователя в локальной переменной name.The component stores the user's name in a local variable, name.
  3. Строка, хранящаяся в name, включается в приветственное сообщение, которое передается в функцию JavaScript, displayWelcome, которая визуализирует приветственное сообщение в тег заголовка.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.

Вызов функции JavaScript voidCall a void JavaScript function

Функции JavaScript, возвращающие значение void (0)/воид 0 или undefine , вызываются с IJSRuntime.InvokeVoidAsync.JavaScript functions that return void(0)/void 0 or undefined are called with IJSRuntime.InvokeVoidAsync.

Обнаружение времени предварительной отрисовки Blazor приложенияDetect when a Blazor app is prerendering

При предварительном отображении серверного приложения Блазор некоторые действия, такие как вызов JavaScript, не возможны, так как соединение с браузером не установлено.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. При предварительной отрисовке компонентов может потребоваться прорисовка по-разному.Components may need to render differently when prerendered.

Чтобы отложить вызовы взаимодействия JavaScript до тех пор, пока не будет установлено подключение к браузеру, можно использовать событие жизненного цикла компонента онафтеррендерасинк.To delay JavaScript interop calls until after the connection with the browser is established, you can use the OnAfterRenderAsync component lifecycle event. Это событие вызывается только после того, как приложение будет полностью визуализировано и установлено клиентское соединение.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");
        }
    }
}

В предыдущем примере кода укажите setElementText функцию JavaScript в элементе <head> wwwroot/index.HTML (блазор Assembly) или pages/_Host. cshtml (блазор 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). Функция вызывается с IJSRuntime.InvokeVoidAsync и не возвращает значение:The function is called with IJSRuntime.InvokeVoidAsync and doesn't return a value:

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

Предупреждение

В предыдущем примере модель DOM (модель DOM) изменяется непосредственно только в демонстрационных целях.The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Непосредственное изменение модели DOM с помощью JavaScript не рекомендуется в большинстве сценариев, поскольку JavaScript может мешать отслеживанию изменений Блазор.Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

В следующем компоненте показано, как использовать взаимодействие JavaScript как часть логики инициализации компонента способом, совместимым с предварительной отрисовкой.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. Компонент показывает, что можно активировать обновление отрисовки из OnAfterRenderAsync.The component shows that it's possible to trigger a rendering update from inside OnAfterRenderAsync. В этом сценарии разработчику следует избегать создания бесконечного цикла.The developer must avoid creating an infinite loop in this scenario.

При вызове JSRuntime.InvokeAsync ElementRef используется только в OnAfterRenderAsync, а не в каком-либо предыдущем методе жизненного цикла, так как нет элемента JavaScript до тех пор, пока не будет визуализирован компонент.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.

Статехасчанжед вызывается для реотрисовки компонента с новым состоянием, полученным из вызова взаимодействия JavaScript.StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call. Код не создает бесконечный цикл, так как StateHasChanged вызывается только при null``infoFromJs.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();
        }
    }
}

В предыдущем примере кода укажите setElementText функцию JavaScript в элементе <head> wwwroot/index.HTML (блазор Assembly) или pages/_Host. cshtml (блазор 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). Функция вызывается с IJSRuntime.InvokeAsync и возвращает значение:The function is called with IJSRuntime.InvokeAsync and returns a value:

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

Предупреждение

В предыдущем примере модель DOM (модель DOM) изменяется непосредственно только в демонстрационных целях.The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Непосредственное изменение модели DOM с помощью JavaScript не рекомендуется в большинстве сценариев, поскольку JavaScript может мешать отслеживанию изменений Блазор.Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

Запись ссылок на элементыCapture references to elements

В некоторых сценариях взаимодействия JS требуются ссылки на элементы HTML.Some JS interop scenarios require references to HTML elements. Например, библиотеке пользовательского интерфейса может требоваться ссылка на элемент для инициализации, или же может потребоваться вызвать API-интерфейсы, подобные Command, в элементе, например focus или 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.

Запишите ссылки на элементы HTML в компоненте, используя следующий подход:Capture references to HTML elements in a component using the following approach:

  • Добавьте атрибут @ref в элемент HTML.Add an @ref attribute to the HTML element.
  • Определите поле типа ElementReference, имя которого соответствует значению атрибута @ref.Define a field of type ElementReference whose name matches the value of the @ref attribute.

В следующем примере показана запись ссылки на элемент <input> username.The following example shows capturing a reference to the username <input> element:

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

@code {
    ElementReference username;
}

Предупреждение

Используйте ссылку на элемент только для изменения содержимого пустого элемента, который не взаимодействует с Blazor.Only use an element reference to mutate the contents of an empty element that doesn't interact with Blazor. Этот сценарий полезен, если сторонний API передает содержимое элементу.This scenario is useful when a 3rd party API supplies content to the element. Поскольку Blazor не взаимодействует с элементом, существует вероятность конфликта между представлением элемента и DOM в Blazor.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.

В следующем примере опасно изменить содержимое неупорядоченного списка (ul), поскольку Blazor взаимодействует с моделью DOM для заполнения элементов списка этого элемента (<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>

Если при взаимодействии JS изменяется содержимое элемента MyList и Blazor пытается применить diff к элементу, различия не будут соответствовать модели 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.

Что касается кода .NET, ElementReference является непрозрачным маркером.As far as .NET code is concerned, an ElementReference is an opaque handle. Единственное , что можно сделать с ElementReference, — передать его в код JavaScript через JS-взаимодействие.The only thing you can do with ElementReference is pass it through to JavaScript code via JS interop. При этом код на стороне JavaScript получает экземпляр HTMLElement, который можно использовать с обычными API DOM.When you do so, the JavaScript-side code receives an HTMLElement instance, which it can use with normal DOM APIs.

Например, следующий код определяет метод расширения .NET, который позволяет установить фокус на элемент:For example, the following code defines a .NET extension method that enables setting the focus on an element:

ексамплежсинтероп. js:exampleJsInterop.js:

window.exampleJsFunctions = {
  focusElement : function (element) {
    element.focus();
  }
}

Чтобы вызвать функцию JavaScript, которая не возвращает значение, используйте IJSRuntime.InvokeVoidAsync.To call a JavaScript function that doesn't return a value, use IJSRuntime.InvokeVoidAsync. Следующий код задает фокус ввода имени пользователя, вызывая предыдущую функцию JavaScript с захваченным ElementReference:The following code sets the focus on the username input by calling the preceding JavaScript function with the captured ElementReference:

@inject IJSRuntime JSRuntime

<input @ref="username" />
<button @onclick="SetFocus">Set focus on username</button>

@code {
    private ElementReference username;

    public async Task SetFocus()
    {
        await JSRuntime.InvokeVoidAsync(
            "exampleJsFunctions.focusElement", username);
    }
}

Чтобы использовать метод расширения, создайте статический метод расширения, который получает экземпляр IJSRuntime:To use an extension method, create a static extension method that receives the IJSRuntime instance:

public static async Task Focus(this ElementReference elementRef, IJSRuntime jsRuntime)
{
    await jsRuntime.InvokeVoidAsync(
        "exampleJsFunctions.focusElement", elementRef);
}

Метод Focus вызывается непосредственно для объекта.The Focus method is called directly on the object. В следующем примере предполагается, что метод Focus доступен из пространства имен JsInteropClasses:The following example assumes that the Focus method is available from the JsInteropClasses namespace:

@inject IJSRuntime JSRuntime
@using JsInteropClasses

<input @ref="username" />
<button @onclick="SetFocus">Set focus on username</button>

@code {
    private ElementReference username;

    public async Task SetFocus()
    {
        await username.Focus(JSRuntime);
    }
}

Важно!

username переменная заполняется только после подготовки компонента к просмотру.The username variable is only populated after the component is rendered. Если незаполненный ElementReference передается в код JavaScript, код JavaScript получает значение null.If an unpopulated ElementReference is passed to JavaScript code, the JavaScript code receives a value of null. Для управления ссылками на элементы после завершения отрисовки компонента (для установки начального фокуса на элемент) используйте методы жизненного цикла компонента онафтеррендерасинк или онафтеррендер.To manipulate element references after the component has finished rendering (to set the initial focus on an element) use the OnAfterRenderAsync or OnAfterRender component lifecycle methods.

При работе с универсальными типами и возвратом значения используйте ValueTask<t >:When working with generic types and returning a value, use ValueTask<T>:

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

GenericMethod вызывается непосредственно для объекта с типом.GenericMethod is called directly on the object with a type. В следующем примере предполагается, что GenericMethod доступен из пространства имен JsInteropClasses:The following example assumes that the GenericMethod is available from the JsInteropClasses namespace:

@inject IJSRuntime JSRuntime
@using JsInteropClasses

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

<p>
    returnValue: @returnValue
</p>

@code {
    private ElementReference username;
    private string returnValue;

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

Вызов методов .NET из функций JavaScriptInvoke .NET methods from JavaScript functions

Статический вызов метода .NETStatic .NET method call

Чтобы вызвать статический метод .NET из JavaScript, используйте функции DotNet.invokeMethod или DotNet.invokeMethodAsync.To invoke a static .NET method from JavaScript, use the DotNet.invokeMethod or DotNet.invokeMethodAsync functions. Передайте идентификатор статического метода, который необходимо вызвать, имя сборки, содержащей функцию, и любые аргументы.Pass in the identifier of the static method you wish to call, the name of the assembly containing the function, and any arguments. Асинхронная версия является предпочтительной для поддержки сценариев Blazor Server.The asynchronous version is preferred to support Blazor Server scenarios. Чтобы вызвать метод .NET из JavaScript, метод .NET должен быть открытым, статическим и иметь атрибут [JSInvokable].To invoke a .NET method from JavaScript, the .NET method must be public, static, and have the [JSInvokable] attribute. По умолчанию идентификатором метода является имя метода, но можно указать другой идентификатор с помощью конструктора JSInvokableAttribute.By default, the method identifier is the method name, but you can specify a different identifier using the JSInvokableAttribute constructor. Вызов открытых универсальных методов в настоящее время не поддерживается.Calling open generic methods isn't currently supported.

Пример приложения включает C# метод для возврата массива ints.The sample app includes a C# method to return an array of ints. К методу применяется атрибут JSInvokable.The JSInvokable attribute is applied to the method.

Pages/жсинтероп. Razor:Pages/JsInterop.razor:

<button type="button" class="btn btn-primary"
        onclick="exampleJsFunctions.returnArrayAsyncJs()">
    Trigger .NET static method ReturnArrayAsync
</button>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

JavaScript, предоставляемый клиенту, вызывает метод C# .NET.JavaScript served to the client invokes the C# .NET method.

wwwroot/ексамплежсинтероп. 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('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
    });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

При выборе переключателя статический метод ретурнаррайасинк .NET изучите выходные данные консоли в средствах веб-разработчика браузера.When the Trigger .NET static method ReturnArrayAsync button is selected, examine the console output in the browser's web developer tools.

Выходные данные консоли:The console output is:

Array(4) [ 1, 2, 3, 4 ]

Четвертое значение массива помещается в массив (data.push(4);), возвращаемый ReturnArrayAsync.The fourth array value is pushed to the array (data.push(4);) returned by ReturnArrayAsync.

Вызов метода экземпляраInstance method call

Кроме того, можно вызывать методы экземпляра .NET из JavaScript.You can also call .NET instance methods from JavaScript. Вызов метода экземпляра .NET из JavaScript:To invoke a .NET instance method from JavaScript:

  • Передайте экземпляр .NET в JavaScript, заключив его в экземпляр DotNetObjectReference.Pass the .NET instance to JavaScript by wrapping it in a DotNetObjectReference instance. Экземпляр .NET передается по ссылке на JavaScript.The .NET instance is passed by reference to JavaScript.
  • Вызывайте методы экземпляра .NET в экземпляре с помощью функций invokeMethod или invokeMethodAsync.Invoke .NET instance methods on the instance using the invokeMethod or invokeMethodAsync functions. Экземпляр .NET можно также передать в качестве аргумента при вызове других методов .NET из JavaScript.The .NET instance can also be passed as an argument when invoking other .NET methods from JavaScript.

Примечание

Пример приложения записывает сообщения в консоль на стороне клиента.The sample app logs messages to the client-side console. В следующих примерах, продемонстрированных в примере приложения, изучите выходные данные консоли браузера в средствах разработчика браузера.For the following examples demonstrated by the sample app, examine the browser's console output in the browser's developer tools.

Когда выбран метод триггера экземпляра .NET хеллохелпер. SayHello , ExampleJsInterop.CallHelloHelperSayHello вызывается и передает имя, Blazor, в метод.When the Trigger .NET instance method HelloHelper.SayHello button is selected, ExampleJsInterop.CallHelloHelperSayHello is called and passes a name, Blazor, to the method.

Pages/жсинтероп. Razor:Pages/JsInterop.razor:

<button type="button" class="btn btn-primary" @onclick="TriggerNetInstanceMethod">
    Trigger .NET instance method HelloHelper.SayHello
</button>

@code {
    public async Task TriggerNetInstanceMethod()
    {
        var exampleJsInterop = new ExampleJsInterop(JSRuntime);
        await exampleJsInterop.CallHelloHelperSayHello("Blazor");
    }
}

CallHelloHelperSayHello вызывает функцию JavaScript sayHello с новым экземпляром HelloHelper.CallHelloHelperSayHello invokes the JavaScript function sayHello with a new instance of HelloHelper.

Жсинтеропклассес/ексамплежсинтероп. CS:JsInteropClasses/ExampleJsInterop.cs:

public class ExampleJsInterop
{
    private readonly IJSRuntime _jsRuntime;

    public ExampleJsInterop(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public ValueTask<string> CallHelloHelperSayHello(string name)
    {
        // sayHello is implemented in wwwroot/exampleJsInterop.js
        return _jsRuntime.InvokeAsync<string>(
            "exampleJsFunctions.sayHello",
            DotNetObjectReference.Create(new HelloHelper(name)));
    }
}

wwwroot/ексамплежсинтероп. 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('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        data.push(4);
          console.log(data);
    });
  },
  sayHello: function (dotnetHelper) {
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};

Имя передается в конструктор HelloHelper, который задает свойство HelloHelper.Name.The name is passed to HelloHelper's constructor, which sets the HelloHelper.Name property. При выполнении sayHello функции JavaScript HelloHelper.SayHello возвращает Hello, {Name}! сообщение, которое записывается в консоль функцией JavaScript.When the JavaScript function sayHello is executed, HelloHelper.SayHello returns the Hello, {Name}! message, which is written to the console by the JavaScript function.

Жсинтеропклассес/хеллохелпер. CS:JsInteropClasses/HelloHelper.cs:

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string SayHello() => $"Hello, {Name}!";
}

Выходные данные консоли в средствах веб-разработчика браузера:Console output in the browser's web developer tools:

Hello, Blazor!

Совместное использование кода взаимодействия в библиотеке классовShare interop code in a class library

Код взаимодействия JS может быть добавлен в библиотеку классов, которая позволяет совместно использовать код в пакете NuGet.JS interop code can be included in a class library, which allows you to share the code in a NuGet package.

Библиотека классов обрабатывает внедрение ресурсов JavaScript в построенную сборку.The class library handles embedding JavaScript resources in the built assembly. Файлы JavaScript помещаются в папку wwwroot .The JavaScript files are placed in the wwwroot folder. Инструментарий выполняет внедрение ресурсов при построении библиотеки.The tooling takes care of embedding the resources when the library is built.

В файле проекта приложения имеется ссылка на созданный пакет NuGet так же, как и на любой пакет NuGet.The built NuGet package is referenced in the app's project file the same way that any NuGet package is referenced. После восстановления пакета код приложения может вызывать JavaScript, как если бы он был C#.After the package is restored, app code can call into JavaScript as if it were C#.

Для получения дополнительной информации см. Библиотеки классов компонентов Razor ASP.NET Core.For more information, see Библиотеки классов компонентов Razor ASP.NET Core.

Вызовы взаимодействия с зафиксированным JSHarden JS interop calls

Взаимодействие с JS может завершиться сбоем из-за ошибок сети и должно рассматриваться как ненадежное.JS interop may fail due to networking errors and should be treated as unreliable. По умолчанию серверное приложение Blazor истекает из JS-вызовов взаимодействия на сервере через одну минуту.By default, a Blazor Server app times out JS interop calls on the server after one minute. Если приложение может допускать более агрессивное время ожидания, например 10 секунд, задайте время ожидания одним из следующих способов.If an app can tolerate a more aggressive timeout, such as 10 seconds, set the timeout using one of the following approaches:

  • Глобально в Startup.ConfigureServicesукажите время ожидания:Globally in Startup.ConfigureServices, specify the timeout:

    services.AddServerSideBlazor(
        options => options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds({SECONDS}));
    
  • Для каждого вызова в коде компонента один вызов может указать время ожидания:Per-invocation in component code, a single call can specify the timeout:

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

Дополнительные сведения об исчерпании ресурсов см. в разделе Безопасные ASP.NET Core Blazor серверные приложения.For more information on resource exhaustion, see Безопасные ASP.NET Core Blazor серверные приложения.

Дополнительные ресурсыAdditional resources