原文連結:https://blazor-university.com/components/component-lifecycles/
元件生命周期
源代碼[1]
Blazor 元件具有許多我們可以重寫以影響應用程式行為的虛拟方法。這些方法在元件生命周期的不同時間執行。下圖概述了這些生命周期方法的流程。
元件生命周期圖
SetParametersAsync
每當父級渲染時執行此方法。
傳遞給元件的參數包含在
ParameterView
中。這是基于傳遞給元件的狀态對伺服器(舉例)進行異步調用的好時機。當您在重寫中調用
base.SetParametersAsync(parameters)
時,将為元件的
[Parameter]
屬性配置設定它們的值。
這也是配置設定預設參數值的正确位置。有關完整說明,請參閱可選路由參數[2]。
OnInitialized / OnInitializedAsync
一旦
ParameterCollection
的狀态被配置設定給元件的
[Parameter]
屬性,這些方法就會被執行。這與
SetParametersAsync
一樣有用,但可以使用元件的狀态。
該方法僅在元件首次建立時執行一次。如果父級稍後更改元件的參數,則跳過此方法。
注意: 當元件是 一個
@page
,并且我們的 Blazor 應用導航到渲染同一頁面的新 URL 時,Blazor 将重用該頁面的目前對象執行個體。因為對象是同一個執行個體,是以 Blazor 不會在對象上調用
IDisposable.Dispose
,也不會再次執行其
OnInitialized
方法。
OnParametersSet / OnParametersSetAsync
如果這是元件的新執行個體,則此方法将在
OnInitializedAsync
之後立即執行。如果它是由于其父元件正在重新渲染而正在重新渲染的現有元件,則不會執行
OnInitialized
方法,而是在
SetParametersAsync
之後立即執行此方法。
StateHasChanged
此方法标記要渲染的元件。
每當元件想通知 Blazor 發生了會導緻渲染輸出不同的更改時,它都會調用此方法。例如,在
Clock
元件中,我們可能會設定一個循環 1 秒的計時器,然後執行
StateHasChanged
以便以正确的時間重新渲染。
另一個用途是訓示 Blazor 通過異步方法部分執行重新渲染。
private async Task GetDataFromMultipleSourcesAsync()
{
var remainingTasks = new HashSet<Task>(CreateTheTasks());
while (remainingTasks.Any())
{
Task completedTask = await Task.WhenAny(remainingTasks);
remainingTasks.Remove(completedTask);
StateHasChanged();
}
}
當等待發生時(第 6 行)或方法完成時(第 10 行),将處理對
StateHasChanged
的調用。
ShouldRender
該方法可以通過傳回
false
來防止元件的渲染樹被重新計算。請注意,第一次建立和渲染元件時不會執行此方法。當我們知道我們的狀态自上次渲染後未更改或僅以會導緻渲染相同輸出的方式更改時,訓示 Blazor 不執行
BuildRenderTree
操作可以節省處理時間并改善使用者體驗。
第一次渲染元件時不執行此方法。
BuildRenderTree
此方法将元件的内容渲染為應渲染給使用者的記憶體表示(RenderTree[3])。
<h1>People</h1>
@foreach(Person currentPerson in people)
{
<ShowPersonDetails Person=@currentPerson/>
}
前面的标記将在渲染樹中添加一個
h1
,其中“People”作為其内容。然後它将為
people
中的每個
Person
建立一個
ShowPersonDetails
的新執行個體。如果我們的元件稍後在
people
中使用附加項重新渲染,則将建立
ShowPersonDetails
元件的新執行個體并将其添加到我們元件的渲染樹中。如果
people
中的項目較少,那麼之前建立的一些
ShowPersonDetails
元件執行個體将從我們元件的渲染樹中丢棄,如果它們實作
IDiposable
,則會對它們執行
Dispose()
。
注意: 為了提高渲染效率,在任何類型的循環中渲染标記時,請盡可能始終使用 @key 指令[4]。
OnAfterRender / OnAfterRenderAsync
每次 Blazor 重新生成元件的渲染樹[5]時,都會執行最後兩個方法。這可能是由于元件的父級重新渲染、使用者與元件互動(例如滑鼠單擊)或元件執行其
StateHasChanged
方法來調用重新渲染的結果。這些方法有一個名為
firstRender
的參數。此參數僅在目前元件上第一次調用該方法時為
true
,從那裡開始它将始終為
false
。在需要附加元件連接配接的情況下(例如,通過 JavaScript),知道這是第一次渲染很有用。直到
OnAfterRender
方法執行之後,才能安全地使用通過 @ref 指令設定的元件的任何引用。
<ChildComponent @ref=MyReferenceToChildComponent/>
@code
{
// This will be null until the OnAfterRender* methods execute
ChildComponent MyReferenceToChildComponent;
}
直到
OnAfterRender
方法被執行并且
firstRender
設定為
true
之後,使用通過 @ref 指令設定的 HTML 元素的任何引用是安全的。
<h1 @ref=MyReferenceToAnHtmlElement>Hello</h1>
@code
{
// This will be null until the OnAfterRender* methods execute
// with firstRender set to true
ElementReference MyReferenceToAnHtmlElement;
}
Dispose
盡管這并不是
ComponentBase
的生命周期方法之一,但如果元件實作了
IDisposable
,那麼一旦從其父級的渲染樹中删除該元件,Blazor 就會執行
Dispose
。要實作
IDisposable
,我們需要将
@implements IDisposable
添加到我們的 razor 檔案中。
@implements IDisposable
<h1>This is MyComponent</h1>
@code {
void IDisposable.Dispose()
{
// Code here
}
}
在異步生命周期方法中等待
請務必注意,Blazor 不會在能夠渲染元件之前等待長時間運作的異步方法完成,而是會盡快觸發渲染。
這使元件能夠在執行背景任務(例如從伺服器檢索資料)時渲染标記供使用者檢視。
SetParametersAsync
-
第一次 await 的動作
繼續生命周期過程
(如果是新執行個體則為 OnInitialized,否則為 OnParametersSet)
-
退出方法的動作
沒有進一步的動作
注意:
base.SetParametersAsync
方法必須在方法中的任何
await
指令之前執行,否則會抛出
InvalidOperationException
。
OnInitializedAsync
-
第一次 await 的動作
渲染元件
-
退出方法的動作
繼續生命周期過程
OnParametersSetAsync
-
第一次 await 的動作
渲染元件
-
退出方法的動作
繼續生命周期過程
OnAfterRenderAsync
-
第一次 await 的動作
沒有進一步的動作
-
退出方法的動作
沒有進一步的動作
簡單的規則是
SetParametersAsync
是唯一不能通過等待任務來暫停生命周期過程的方法。所有其他異步方法都可以暫停生命周期過程,直到執行退出該方法,并且第一個
await
将通過
BuildRenderTree
進行渲染,以防止使用者不得不等待檢視更新。
OnAfterRenderAsync
可能看起來像一個異常,因為它在任何一種情況下都不會執行進一步的操作。如果我們考慮渲染是執行鍊的末端這一事實,那麼我們可以将其視為完成鍊而不是什麼都不做。至于等待渲染,如果需要,程式員必須通過調用
StateHasChanged
顯式完成,否則
OnAfterRenderAsync
中的等待将導緻無限循環。
具有異步等待的元件生命周期
異步方法和多個等待
Blazor 在異步方法中的
await
上執行的代碼隻會在第一次
await
時執行。後續等待不會導緻多次渲染。例如
protected override async Task OnParametersSetAsync()
{
// Automatically renders when next line starts to await
await Task.Delay(1000);
// No automatic render when next line starts to await
await Task.Delay(1000);
// No automatic render when next line starts to await
await Task.Delay(1000);
}
如果我們想在其他點進行渲染,那麼我們必須在所有其他
await
語句之前調用
StateHasChanged
。
protected override async Task OnParametersSetAsync()
{
// Automatically renders when next line starts to await
await Task.Delay(1000);
// Explicitly render when next line starts to await
StateHasChanged();
await Task.Delay(1000);
// Explicitly render when next line starts to await
StateHasChanged();
await Task.Delay(1000);
}