Содержание
Все компоненты Razor, с которыми мы работали ранее, наследуются от базового абстрактного класса Component
. У этого класса определены методы, благодаря которым мы можем управлять жизненным циклом компонента Razor.
Методы жизненного цикла компонента Razor
У класса Component
определены следующие методы жизненного цикла:
On
иInitialized() On
Initialized Async() On
иParameters Set() On
Parameters Set Async() On
иAfter Render(bool firstRender) OnAfterRenderAsync(bool firstRender)
при этом синхронные версии методов вызываются перед асинхронными. Рассмотрим эти методы более подробно.
On Initialized() и On Initialized Async()
Методы OnInitialized()
и OnInitializedAsync()
используются исключительно для инициализации компонента на протяжении всего срока службы экземпляра. Используя эти методы, можно, например, инициализировать значения каких-либо свойств или полей компонента. Если используется синхронная версия метода, то инициализация родительского компонента гарантировано будет завершена раньше, чем дочернего. В случае использования асинхронных версий предугадать какой компонент будет инициализирован первым невозможно, так как очередность инициализации будет зависеть от кода метода инициализации.
Чтобы продемонстрировать вышесказанное, создадим новое приложение Blazor Hybrid и изменим код компонентов Home и Counter следующим образом:
Counter
@page "/counter" <h1>@message</h1> <p role="status">Current count: @CurrentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { string message = ""; [Parameter] public int CurrentCount { get; set; } = 0; protected override void OnInitialized() { message = $"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}"; } private void IncrementCount() { CurrentCount++; } }
Home
@page "/" <h1>@message</h1> Welcome to your new app. <Counter CurrentCount="10"></Counter> @code{ string message = ""; protected override void OnInitialized() { message = $"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}"; } }
Теперь Counter
является дочерним компонентом для Home
. У обоих компонентов мы переопределили синхронный метод OnInitialized()
в которых выводим точное время выполнения метода. Запустим приложение и посмотрим на результат:
Сколько бы раз мы не перезапускали приложение компонент Counter
будет инициализирован всегда позже, чем Home
. Теперь попробуем применить асинхронные версии методов следующим образом:
Метод OnInitializedAsync() компонента Counter
protected override async Task OnInitializedAsync() { await Task.Delay(Random.Shared.Next(100,1000)); message = $"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}"; }
Метод OnInitializedAsync() компонента Home
protected override async Task OnInitializedAsync() { await Task.Delay(Random.Shared.Next(100,1000)); message = $"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}"; }
Теперь методы OnInitializedAsync()
у обоих методов будут «зависать» на случайное количество миллисекунд, поэтому предугадать какой из компонентов будет инициализирован первым становится невозможно. Например, можно получить вот такой результат:
Как видно по рисунку, родительский компонент инициализирован позже дочернего.
OnParametersSet() и OnParametersSetAsync()
Эти методы вызываются после инициализации компонента в OnInitialized()
или OnInitializedAsync()
и после того, как родительский компонент устанавливает параметры дочернего компонента. Чтобы продемонстрировать очередность выполнения методов, снова изменим компоненты Home
и Counter
следующим образом:
Counter
@page "/counter" <ul> @foreach (string s in message) { <li>@s</li> } </ul> <p role="status">Current count: @CurrentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { List<string> message = []; [Parameter] public int CurrentCount { get; set; } = 0; protected override void OnInitialized() { message.Add($"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}"); } protected override void OnParametersSet() { message.Add($"Параметры компонента Counter установлены {DateTime.Now:hh:mm:ss:fff}"); } private void IncrementCount() { CurrentCount++; } }
Home
@page "/" <h3> <ul> @foreach (var s in message) { <li>@s</li> } </ul> </h3> Welcome to your new app. <Counter CurrentCount="10"></Counter> @code{ List<string> message = []; protected override void OnInitialized() { message.Add($"Компонент Home инициализирован {DateTime.Now:hh:mm:ss:fff}"); } protected override void OnParametersSet() { message.Add($"Параметры компонента Home установлены {DateTime.Now:hh:mm:ss:fff}"); } }
Так как мы используем синхронные методы, то все методы будут выполняться по порядку — сначала пройдет инициализация Home
, затем сработает метод OnParametersSet()
, затем инициализируется Counter
и ему будут назначены параметры:
On After Render(bool firstRender) и OnAfterRenderAsync(bool firstRender)
Методы OnAfterRender()
и OnAfterRenderAsync()
вызываются после интерактивной отрисовки компонента и завершения обновления пользовательского интерфейса. В этом методе рекомендуется производить какие-либо дополнительные шаги по инициализации компонента, например, для вызова скриптов JS, которые взаимодействуют с уже отрисованными элементами DOM.
При этом, параметр firstRender
устанавливается в значение true
при первом рендеринге экземпляра компонента и может быть использован для обеспечения того, чтобы работа по инициализации выполнялась только один раз.
Эти методы вызываются последними. Например, переопределим этот метод в компоненте Counter
:
@page "/counter" <ul> @foreach (string s in message) { <li>@s</li> } </ul> <p role="status">Current count: @CurrentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { List<string> message = []; [Parameter] public int CurrentCount { get; set; } = 0; protected override void OnInitialized() { message.Add($"Компонент Counter инициализирован {DateTime.Now:hh:mm:ss:fff}"); } protected override void OnParametersSet() { message.Add($"Параметры компонента Counter установлены {DateTime.Now:hh:mm:ss:fff}"); } private void IncrementCount() { CurrentCount++; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); } else { message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); } } }
Теперь запустим приложение и посмотрим на результат:
Так как компонент уже был отрисован перед выполнением метода OnAfterRender()
, то строка «Рендеринг компонента…» была добавлена в список message
, но не была выведена на страницу. Чтобы увидеть время рендеринга компонента можно поступить двумя способами:
1. Заставить компонент перерисоваться, например, нажав кнопку «Click me». Результат будет следующим:
2. Вызвать метод StateHasChanged()
базового класса, чтобы заставить компонент перерисоваться, например, так:
protected override void OnAfterRender(bool firstRender) { if (firstRender) { message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); StateHasChanged(); } else { message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); } }
Теперь строки из метода OnAfterRender()
будут показываться сразу после рендеринга:
Метод StateHasChanged()
стоит использовать с осторожностью, чтобы не получить бесконечный рендеринг компонента. Например, вот так делать НЕ надо:
protected override void OnAfterRender(bool firstRender) { if (firstRender) { message.Add($"Рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); } else { message.Add($"Повторный рендеринг компонента Counter завершен {DateTime.Now:hh:mm:ss:fff}"); } StateHasChanged(); //получаем бесконечный рендеринг }
Результат будет вот таким:
Итого
Компоненты Razor имеют четко определенный жизненный цикл, воздействовать на который мы можем переопределением методов жизненного цикла. Порядок выполнение методов от инициализации компонента до его удаления из интерфейса следующий: OnInitialized()
—> OnParametersSet()
—> OnAfterRender(firstRender = true)
—> OnAfterRender(firstRender = false)
—> ….. OnAfterRender(firstRender = false)
.