Компоненты предварительной ASP.NET Core Razor

В этой статье описываются Razor сценарии предварительной подготовки компонентов для компонентов, отрисованных на сервере, в Blazor веб-приложения.

Предварительная отрисовка — это процесс первоначальной отрисовки содержимого страницы на сервере без включения обработчиков событий для отрисованных элементов управления. Сервер выводит HTML-интерфейс страницы как можно скорее в ответ на первоначальный запрос, что делает приложение более адаптивным к пользователям. Предварительная подготовка также может улучшить оптимизацию поисковой системы (SEO), отрисовав содержимое для первоначального HTTP-ответа, используемого поисковыми системами для вычисления ранжирования страниц.

Сохранение предварительно отрисованного состояния

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

Рассмотрим следующий PrerenderedCounter1 компонент счетчика. Компонент задает начальное значение случайного счетчика во время предварительной подготовки в OnInitialized методе жизненного цикла. SignalR После установки подключения к клиенту рендердеры компонентов и начальное значение счетчика заменяются при OnInitialized выполнении во второй раз.

PrerenderedCounter1.razor:

@page "/prerendered-counter-1"
@rendermode @(new InteractiveServerRenderMode(prerender: true))
@inject ILogger<PrerenderedCounter1> Logger

<PageTitle>Prerendered Counter 1</PageTitle>

<h1>Prerendered Counter 1</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;

    protected override void OnInitialized()
    {
        currentCount = Random.Shared.Next(100);
        Logger.LogInformation("currentCount set to {Count}", currentCount);
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Запустите приложение и проверьте ведение журнала из компонента. Ниже приведен пример выходных данных.

Примечание.

Если приложение принимает интерактивную (расширенную) маршрутизацию, а страница достигается с помощью внутренней навигации, предварительная подготовка не возникает. Поэтому необходимо выполнить полную перезагрузку страницы для PrerenderedCounter1 компонента, чтобы просмотреть следующие выходные данные.

info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92

Первое количество зарегистрированных журналов происходит во время предварительной подготовки. Счетчик устанавливается снова после предварительной отрисовки, когда компонент rerendered. Есть также мерцание в пользовательском интерфейсе, когда количество обновлений от 41 до 92.

Чтобы сохранить начальное значение счетчика во время предварительной отрисовки, Blazor поддерживает сохранение состояния на предварительно созданной странице с помощью PersistentComponentState службы (а также для компонентов, внедренных в страницы или представления Razor приложений Pages или MVC, вспомогательного тега состояния компонента).

Чтобы сохранить предварительно созданное состояние, определите, какое состояние следует сохранить с помощью PersistentComponentState службы. PersistentComponentState.RegisterOnPersisting регистрирует обратный вызов для сохранения состояния компонента до приостановки приложения. Состояние извлекается при возобновлении работы приложения.

В следующем примере показан общий шаблон:

  • Заполнитель {TYPE} представляет тип сохраняемых данных.
  • Заполнитель {TOKEN} — это строка идентификатора состояния. Рекомендуется использовать nameof({VARIABLE}), где {VARIABLE} заполнитель — это имя переменной, содержащей состояние. Использование nameof() для идентификатора состояния позволяет избежать использования кавычек.
@implements IDisposable
@inject PersistentComponentState ApplicationState

...

@code {
    private {TYPE} data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistData);

        if (!ApplicationState.TryTakeFromJson<{TYPE}>(
            "{TOKEN}", out var restored))
        {
            data = await ...;
        }
        else
        {
            data = restored!;
        }
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson("{TOKEN}", data);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

Следующий пример компонента счетчика сохраняет состояние счетчика во время предварительной подготовки и извлекает состояние для инициализации компонента.

PrerenderedCounter2.razor:

@page "/prerendered-counter-2"
@implements IDisposable
@inject ILogger<PrerenderedCounter2> Logger
@inject PersistentComponentState ApplicationState

<PageTitle>Prerendered Counter 2</PageTitle>

<h1>Prerendered Counter 2</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override void OnInitialized()
    {
        persistingSubscription =
            ApplicationState.RegisterOnPersisting(PersistCount);

        if (!ApplicationState.TryTakeFromJson<int>(
            nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
            Logger.LogInformation("currentCount set to {Count}", currentCount);
        }
        else
        {
            currentCount = restoredCount!;
            Logger.LogInformation("currentCount restored to {Count}", currentCount);
        }
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose() => persistingSubscription.Dispose();

    private void IncrementCount()
    {
        currentCount++;
    }
}

При выполнении currentCount компонента устанавливается только один раз во время предварительной подготовки. Значение восстанавливается при переборе компонента. Ниже приведен пример выходных данных.

Примечание.

Если приложение принимает интерактивную маршрутизацию, а страница достигается через внутреннюю навигацию, предварительная отрисовка не возникает. Поэтому необходимо выполнить полную перезагрузку страницы для PrerenderedCounter2 компонента, чтобы просмотреть следующие выходные данные.

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96

При инициализации компонентов с тем же состоянием, которое использовалось во время предварительной отрисовки, все ресурсоемкие шаги инициализации выполняются только один раз. Отрисованный пользовательский интерфейс также соответствует предварительно отрисованному пользовательскому интерфейсу, поэтому в браузере нет никаких мерцаний.

Компоненты, внедренные в страницы и представления (Razor Pages/MVC)

Для компонентов, внедренных в страницу или представление Razor приложения Pages или MVC, необходимо добавить вспомогатель тега состояния компонента с <persist-component-state /> тегом HTML внутри закрывающего </body> тега макета приложения. Это необходимо только для Razor приложений Pages и MVC. Дополнительные сведения см . в справке по тегу состояния компонента в ASP.NET Core.

Pages/Shared/_Layout.cshtml:

<body>
    ...

    <persist-component-state />
</body>

Интерактивная маршрутизация и предварительная подготовка

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

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

Руководство по предварительной подготовке

Руководство по предварительной Blazor подготовке организовано в документации по темам. Следующие ссылки охватывают все рекомендации по предварительной подготовке в документации, заданной по теме: