Поделиться через


Взаимодействие ASP.NET Core Blazor с JavaScript (JS-взаимодействие)

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.

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

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 9 этой статьи.

Приложение Blazor может вызывать функции JavaScript (JS) из методов .NET и методы .NET из функций JS. Такой подход называется взаимодействием с JavaScript (JS-взаимодействием).

Дополнительные сведения о JS-взаимодействии можно найти в следующих статьях:

Примечание.

API взаимодействия JavaScript [JSImport]/[JSExport] доступен для клиентских компонентов в ASP.NET Core в .NET 7 или более поздней версии.

Дополнительные сведения см. в статье JavaScript JSImport/JSExport interop with ASP.NET Core Blazor.

Сжатие для интерактивных компонентов сервера с ненадежными данными

С помощью сжатия, который включен по умолчанию, избегайте создания безопасных (прошедших проверку подлинности или авторизованных) интерактивных компонентов на стороне сервера, отрисовывающих данные из ненадежных источников. Ненадежные источники включают параметры маршрута, строки запроса, данные из JS взаимодействия и любой другой источник данных, которые сторонний пользователь может контролировать (базы данных, внешние службы). Дополнительные сведения см. в руководстве ASP.NET Основных BlazorSignalR руководствах по устранению угроз и устранении угроз для ASP.NET интерактивной отрисовки на стороне сервера ASP.NET CoreBlazor.

Абстракции взаимодействия JavaScript и пакет функций

Пакет () (Microsoft.JSInteropnpmjs.comпакет NuGet) предоставляет абстракции и функции взаимодействия между кодом .NET и JavaScript ().JS@microsoft/dotnet-js-interop Справочный dotnet/aspnetcore источник доступен в репозитории GitHub (/src/JSInterop папка). Дополнительные сведения см. в файле репозитория README.md GitHub.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Дополнительные ресурсы для написания JS скриптов взаимодействия в TypeScript:

Взаимодействие с DOM

Изменить DOM с помощью JavaScript (JS) можно только в том случае, если объект не взаимодействует Blazor. Blazor поддерживает представления модели DOM и взаимодействует с объектами DOM напрямую. Если элемент, отображаемый Blazor, изменяется извне с помощью JS напрямую или путем взаимодействия с JS, модель DOM может больше не соответствовать внутреннему представлению Blazor, что может привести к неопределенному поведению. Неопределенное поведение может просто мешать представлению элементов или их функций, а может и представлять угрозу безопасности для приложения или сервера.

Это руководство применяется не только к вашему коду взаимодействия JS, но и к любым библиотекам JS, используемым приложением, включая все, что предоставляется сторонней платформой, например Bootstrap JS и jQuery.

В некоторых примерах документации взаимодействие JS используется для изменения элемента исключительно для демонстрации как часть примера. В таких случаях в тексте появляется предупреждение.

Дополнительные сведения см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.

Класс JavaScript с полем функции типа

Класс JavaScript с полем функции типа не поддерживается взаимодействием BlazorJS . Используйте функции Javascript в классах.

Неподдерживаемый: GreetingHelpers.sayHello в следующем классе как поле функции типа не обнаруживается с помощью BlazorJS функции взаимодействия и не может выполняться из кода C#:

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

в следующем классе в качестве функции поддерживается:

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

Также поддерживаются функции со стрелками:

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

Избегайте встроенных обработчиков событий

Функцию JavaScript можно вызывать непосредственно из встроенного обработчика событий. В следующем примере используется функция JavaScript, alertUser вызываемая при выборе кнопки пользователем:

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

Однако использование встроенных обработчиков событий является плохим вариантом проектирования для вызова функций JavaScript:

Рекомендуется избегать встроенных обработчиков событий в пользу подходов, которые назначают обработчики в JavaScript, addEventListenerкак показано в следующем примере:

AlertUser.razor.js:

export function alertUser() {
  alert('The button was selected!');
}

export function addHandlers() {
  const btn = document.getElementById("btn");
  btn.addEventListener("click", alertUser);
}

AlertUser.razor:

@page "/alert-user"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>Alert User</h1>

<p>
    <button id="btn">Click Me!</button>
</p>

@code {
    private IJSObjectReference? module;

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./Components/Pages/AlertUser.razor.js");

            await module.InvokeVoidAsync("addHandlers");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }
    }
}

В предыдущем примере JSDisconnectedException происходит ловушка во время удаления модуля в случае BlazorSignalR потери канала. Если предыдущий код используется в Blazor WebAssembly приложении, нет подключения к потере, SignalR поэтому можно удалить блок и оставить строку, которая удаляет try-catch модуль (await module.DisposeAsync();). Дополнительные сведения см. в разделе Взаимодействие JavaScript приложения Blazor ASP.NET Core (взаимодействие JS).

Дополнительные сведения см. на следующих ресурсах:

Асинхронные вызовы JavaScript

JS Вызовы взаимодействия являются асинхронными, независимо от того, синхронный или асинхронный код. Вызовы являются асинхронными, чтобы обеспечить совместимость компонентов между серверными и клиентскими моделями отрисовки. При внедрении отрисовки на стороне сервера вызовы взаимодействия должны быть асинхронными, JS так как они отправляются через сетевое подключение. Для приложений, использующих исключительно отрисовку на стороне клиента, поддерживаются синхронные JS вызовы взаимодействия.

Дополнительные сведения см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.

Сериализация объектов

Blazor использует System.Text.Json для сериализации в соответствии с приведенными ниже требованиями и поведениями по умолчанию.

  • Типы должны иметь конструктор по умолчанию, методы доступа get/set должны быть общедоступными, а поля никогда не сериализуются.
  • Глобальная сериализация по умолчанию не настраивается во избежание повреждения существующих библиотек компонентов, негативного влияния на производительность и безопасность и снижения уровня надежности.
  • Сериализация имен членов .NET приводит к именам ключей JSON в нижнем регистре.
  • JSON десериализируется как JsonElement экземпляры C#, что позволяет использовать смешанный регистр. Внутреннее приведение свойств модели C# работает должным образом, несмотря на различия между именами ключей JSON и именами свойств C#.
  • Сложные типы платформ, например KeyValuePair, могут быть обрезаны IL Trimmer при публикации и не присутствуют для JS взаимодействия. Рекомендуется создавать настраиваемые типы для типов, которые обрезает триммер IL.
  • Blazorвсегда зависит от отражения сериализации JSON, в том числе при использовании создания источника C#. false При JsonSerializerIsReflectionEnabledByDefault попытке сериализации параметр в файле проекта приложения приводит к ошибке.

Для пользовательской сериализации доступен API JsonConverter. Свойства могут быть помечены атрибутом [JsonConverter] для переопределения сериализации по умолчанию для существующего типа данных.

Дополнительные сведения см. в ресурсах в документации по .NET:

Blazor поддерживает оптимизированное взаимодействие с массивом байтов JS, которое позволяет избежать кодирования и декодирования массивов байтов в Base64. Приложение может применять пользовательскую сериализацию и передавать результирующие байты. Дополнительные сведения см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.

При быстрой сериализации большого объема объектов .NET либо в случае необходимости сериализации больших объектов .NET или значительного количества объектов .NET Blazor поддерживает демаршалированное взаимодействие с JS. Дополнительные сведения см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.

Задачи очистки DOM во время удаления компонентов

Не выполняйте JS код взаимодействия для задач очистки DOM во время удаления компонентов. Вместо этого используйте MutationObserver шаблон в JavaScript (JS) на клиенте по следующим причинам:

  • Компонент, возможно, был удален из DOM по времени выполнения кода очистки Dispose{Async}.
  • Во время отрисовки Blazor на стороне сервера средство отрисовки может быть удалено платформой по времени выполнения кода очистки Dispose{Async}.

Шаблон MutationObserver позволяет запускать функцию при удалении элемента из DOM.

В следующем примере DOMCleanup компонент:

  • Содержит объект <div> с числом id cleanupDiv. Элемент <div> удаляется из DOM вместе с rest разметкой DOM компонента при удалении компонента из DOM.
  • DOMCleanupJS Загружает класс из DOMCleanup.razor.js файла и вызывает ее createObserver функцию для настройки обратного MutationObserver вызова. Эти задачи выполняются в методе жизненного OnAfterRenderAsync цикла.

DOMCleanup.razor:

@page "/dom-cleanup"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>DOM Cleanup Example</h1>

<div id="cleanupDiv"></div>

@code {
    private IJSObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./Components/Pages/DOMCleanup.razor.js");

            await module.InvokeVoidAsync("DOMCleanup.createObserver");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }
    }
}

В предыдущем примере JSDisconnectedException происходит ловушка во время удаления модуля в случае BlazorSignalR потери канала. Если предыдущий код используется в Blazor WebAssembly приложении, нет подключения к потере, SignalR поэтому можно удалить блок и оставить строку, которая удаляет try-catch модуль (await module.DisposeAsync();). Дополнительные сведения см. в разделе Взаимодействие JavaScript приложения Blazor ASP.NET Core (взаимодействие JS).

В следующем примере MutationObserver обратный вызов выполняется при каждом изменении DOM. Выполните код очистки, когда if инструкция подтверждает, что целевой элемент (cleanupDiv) был удален (if (targetRemoved) { ... }). Важно отключить и удалить MutationObserver память после выполнения кода очистки.

DOMCleanup.razor.js помещается параллельно с предыдущим DOMCleanup компонентом:

export class DOMCleanup {
  static observer;

  static createObserver() {
    const target = document.querySelector('#cleanupDiv');

    this.observer = new MutationObserver(function (mutations) {
      const targetRemoved = mutations.some(function (mutation) {
        const nodes = Array.from(mutation.removedNodes);
        return nodes.indexOf(target) !== -1;
      });

      if (targetRemoved) {
        // Cleanup resources here
        // ...

        // Disconnect and delete MutationObserver
        this.observer && this.observer.disconnect();
        delete this.observer;
      }
    });

    this.observer.observe(target.parentNode, { childList: true });
  }
}

window.DOMCleanup = DOMCleanup;

Вызовы взаимодействия JavaScript без канала

Этот раздел применяется только к серверным приложениям.

Вызовы взаимодействия JavaScriptJS () не могут быть выданы после BlazorSignalR отключения канала. Без канала во время удаления компонентов или в любое другое время, когда канал не существует, следующий метод вызывает сбой и записывает сообщение о том, что канал отключен как:JSDisconnectedException

Чтобы избежать ведения журнала JSDisconnectedException или регистрации пользовательских сведений, перехватите исключение в инструкции try-catch .

В следующем примере удаления компонентов:

  • Серверный компонент реализует IAsyncDisposable.
  • moduleIJSObjectReference— это модульJS.
  • JSDisconnectedException перехватываются и не регистрируются.
  • Кроме того, вы можете записывать пользовательские сведения в инструкцию catch на любом выбранном уровне журнала. В следующем примере не регистрируется пользовательская информация, так как предполагается, что разработчик не заботится о том, когда или где каналы отключены во время удаления компонентов.
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

Если необходимо очистить собственные JS объекты или выполнить другой JS код на клиенте после потери канала в серверном Blazor приложении, используйте MutationObserver шаблон JS в клиенте. Шаблон MutationObserver позволяет запускать функцию при удалении элемента из DOM.

Дополнительные сведения см. в следующих статьях:

Кэшированные файлы JavaScript

Файлы JavaScript (JS) и другие статические ресурсы обычно не кэшируются на клиентах во время разработки в среде Development. Во время разработки запросы статических ресурсов содержат заголовок Cache-Control со значением no-cache или max-age со значением, равным нулю (0).

На этапе производства в среде Production файлы JS обычно кэшируются клиентами.

Чтобы отключить кэширование на стороне клиента в браузерах, разработчики, как правило, используют один из следующих подходов:

  • Отключение кэширования, когда открыта консоль средств разработчика браузера. Рекомендации см. в документации по средствам разработчика для каждого средства поддержки браузера:
  • Обновление вручную в браузере любой веб-страницы приложения Blazor для перезагрузки файлов JS с сервера. ПО промежуточного слоя HTTP ASP.NET всегда учитывает допустимый некэшированный заголовок Cache-Control, отправляемый клиентом.

Дополнительные сведения см. в разделе:

Ограничения размера для вызовов взаимодействия с JavaScript

Этот раздел относится только к интерактивным компонентам в серверных приложениях. Для клиентских компонентов платформа не накладывает ограничение на размер входных и выходных данных JavaScript (JS).

Для интерактивных компонентов в серверных приложениях вызовы взаимодействия, передавающие данные от клиента на сервер, JS ограничены максимальным размером входящих SignalR сообщений, разрешенным для методов концентратора ( HubOptions.MaximumReceiveMessageSize по умолчанию: 32 КБ). Если размер сообщений SignalR (из JS в .NET) превышает MaximumReceiveMessageSize, возникает ошибка. Платформа не накладывает ограничение на размер сообщений SignalR от концентратора клиенту. Дополнительные сведения об ограничении размера, сообщениях об ошибках и рекомендациях по работе с ограничениями размера сообщений см . в ASP.NET основных BlazorSignalR руководствах.

Определение места выполнения приложения

Если это важно для приложения, чтобы узнать, где выполняется код для JS вызовов взаимодействия, используйте для OperatingSystem.IsBrowser определения, выполняется ли компонент в контексте браузера в WebAssembly.