ASP.NET Core Blazor 生命週期ASP.NET Core Blazor lifecycle

By Luke LathamDaniel RothBy Luke Latham and Daniel Roth

此 Blazor 架構包含同步和非同步生命週期方法。The Blazor framework includes synchronous and asynchronous lifecycle methods. 覆寫生命週期方法,在元件初始化和轉譯期間,對元件執行其他作業。Override lifecycle methods to perform additional operations on components during component initialization and rendering.

生命週期方法Lifecycle methods

元件初始化方法Component initialization methods

OnInitializedAsyncOnInitialized當元件從其父元件接收到其初始參數之後,就會叫用和。OnInitializedAsync and OnInitialized are invoked when the component is initialized after having received its initial parameters from its parent component. OnInitializedAsync當元件執行非同步作業時使用,而且應該在作業完成時重新整理。Use OnInitializedAsync when the component performs an asynchronous operation and should refresh when the operation is completed.

針對同步作業,覆寫 OnInitializedFor a synchronous operation, override OnInitialized:

protected override void OnInitialized()
{
    ...
}

若要執行非同步作業,請覆寫 OnInitializedAsync 並在作業上使用await運算子:To perform an asynchronous operation, override OnInitializedAsync and use the await operator on the operation:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor其內容呼叫呈現 OnInitializedAsync 兩次 的伺服器應用程式: Server apps that prerender their content call OnInitializedAsync twice:

  • 當元件一開始以靜態方式轉譯為頁面的一部分時。Once when the component is initially rendered statically as part of the page.
  • 第二次當瀏覽器建立與伺服器的連接時。A second time when the browser establishes a connection back to the server.

若要防止中的開發人員程式碼執行 OnInitializedAsync 兩次,請參閱在預做重新設定狀態一節。To prevent developer code in OnInitializedAsync from running twice, see the Stateful reconnection after prerendering section.

在預先 Blazor 處理伺服器應用程式時,因為尚未建立與瀏覽器的連接,所以無法執行某些動作(例如呼叫 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. 如需詳細資訊,請參閱偵測應用程式何時進行預呈現一節。For more information, see the Detect when the app is prerendering section.

如果已設定任何事件處理常式,請將它們解除鎖定以供處置。If any event handlers are set up, unhook them on disposal. 如需詳細資訊,請參閱使用 IDisposable 的元件處置一節。For more information, see the Component disposal with IDisposable section.

設定參數之前Before parameters are set

SetParametersAsync在轉譯樹狀結構中設定元件的父系所提供的參數:SetParametersAsync sets parameters supplied by the component's parent in the render tree:

public override async Task SetParametersAsync(ParameterView parameters)
{
    await ...

    await base.SetParametersAsync(parameters);
}

ParameterView每次呼叫時,包含一組完整的參數值 SetParametersAsyncParameterView contains the entire set of parameter values each time SetParametersAsync is called.

的預設執行 SetParametersAsync [Parameter] [CascadingParameter] 會使用在中具有對應值的或屬性,來設定每個屬性的值 ParameterViewThe default implementation of SetParametersAsync sets the value of each property with the [Parameter] or [CascadingParameter] attribute that has a corresponding value in the ParameterView. 在中沒有對應值的參數 ParameterView 會保持不變。Parameters that don't have a corresponding value in ParameterView are left unchanged.

如果基底。不會叫用 SetParametersAync,自訂程式碼可以用任何需要的方式解讀傳入的參數值。If base.SetParametersAync isn't invoked, the custom code can interpret the incoming parameters value in any way required. 例如,不需要將傳入的參數指派給類別的屬性。For example, there's no requirement to assign the incoming parameters to the properties on the class.

如果已設定任何事件處理常式,請將它們解除鎖定以供處置。If any event handlers are set up, unhook them on disposal. 如需詳細資訊,請參閱使用 IDisposable 的元件處置一節。For more information, see the Component disposal with IDisposable section.

設定參數之後After parameters are set

OnParametersSetAsyncOnParametersSet系統會呼叫和:OnParametersSetAsync and OnParametersSet are called:

  • 當元件初始化並從其父元件收到第一組參數時。When the component is initialized and has received its first set of parameters from its parent component.
  • 當父元件重新呈現和提供時:When the parent component re-renders and supplies:
    • 只有已知的基本不可變類型,其中至少有一個參數已變更。Only known primitive immutable types of which at least one parameter has changed.
    • 任何複雜類型的參數。Any complex-typed parameters. 架構無法得知複雜型別參數的值是否在內部變動,因此它會將參數集視為已變更。The framework can't know whether the values of a complex-typed parameter have mutated internally, so it treats the parameter set as changed.
protected override async Task OnParametersSetAsync()
{
    await ...
}

注意

當套用參數和屬性值時,必須在生命週期事件期間進行非同步工作 OnParametersSetAsyncAsynchronous work when applying parameters and property values must occur during the OnParametersSetAsync lifecycle event.

protected override void OnParametersSet()
{
    ...
}

如果已設定任何事件處理常式,請將它們解除鎖定以供處置。If any event handlers are set up, unhook them on disposal. 如需詳細資訊,請參閱使用 IDisposable 的元件處置一節。For more information, see the Component disposal with IDisposable section.

元件呈現之後After component render

OnAfterRenderAsyncOnAfterRender 元件完成呈現之後,會呼叫和。OnAfterRenderAsync and OnAfterRender are called after a component has finished rendering. 此時會填入元素和元件參考。Element and component references are populated at this point. 使用此階段來執行使用轉譯內容的其他初始化步驟,例如啟用在轉譯的 DOM 元素上操作的協力廠商 JavaScript 程式庫。Use this stage to perform additional initialization steps using the rendered content, such as activating third-party JavaScript libraries that operate on the rendered DOM elements.

firstRender和的參數 OnAfterRenderAsync OnAfterRenderThe firstRender parameter for OnAfterRenderAsync and OnAfterRender:

  • true 在第一次呈現元件實例時設定為。Is set to true the first time that the component instance is rendered.
  • 可以用來確保初始化工作只會執行一次。Can be used to ensure that initialization work is only performed once.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

注意

在生命週期事件期間,必須立即執行非同步工作 OnAfterRenderAsyncAsynchronous work immediately after rendering must occur during the OnAfterRenderAsync lifecycle event.

即使您 Task 從傳回 OnAfterRenderAsync ,架構也不會在該工作完成後,為您的元件排程進一步的轉譯週期。Even if you return a Task from OnAfterRenderAsync, the framework doesn't schedule a further render cycle for your component once that task completes. 這是為了避免無限的呈現迴圈。This is to avoid an infinite render loop. 它與其他生命週期方法不同,後者會在傳回的工作完成後,排程進一步的轉譯週期。It's different from the other lifecycle methods, which schedule a further render cycle once the returned task completes.

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        ...
    }
}

OnAfterRenderOnAfterRenderAsync 伺服器上進行預呈現時,不會呼叫和。OnAfterRender and OnAfterRenderAsync aren't called when prerendering on the server.

如果已設定任何事件處理常式,請將它們解除鎖定以供處置。If any event handlers are set up, unhook them on disposal. 如需詳細資訊,請參閱使用 IDisposable 的元件處置一節。For more information, see the Component disposal with IDisposable section.

隱藏 UI 重新整理Suppress UI refreshing

覆寫 ShouldRender 以隱藏 UI 重新整理。Override ShouldRender to suppress UI refreshing. 如果執行傳回 true ,則會重新整理 UI:If the implementation returns true, the UI is refreshed:

protected override bool ShouldRender()
{
    var renderUI = true;

    return renderUI;
}

ShouldRender每次呈現元件時,都會呼叫。ShouldRender is called each time the component is rendered.

即使 ShouldRender 已覆寫,元件一律會一開始呈現。Even if ShouldRender is overridden, the component is always initially rendered.

如需詳細資訊,請參閱<xref:performance/blazor/webassembly-best-practices#avoid-unnecessary-component-renders>。For more information, see <xref:performance/blazor/webassembly-best-practices#avoid-unnecessary-component-renders>.

狀態變更State changes

StateHasChanged通知元件其狀態已變更。StateHasChanged notifies the component that its state has changed. 當適用時,呼叫 StateHasChanged 會導致元件重新顯示。When applicable, calling StateHasChanged causes the component to be rerendered.

處理轉譯時的未完成非同步動作Handle incomplete async actions at render

在呈現元件之前,在生命週期事件中執行的非同步動作可能尚未完成。Asynchronous actions performed in lifecycle events might not have completed before the component is rendered. null當生命週期方法正在執行時,物件可能會或未完全填入資料。Objects might be null or incompletely populated with data while the lifecycle method is executing. 提供轉譯邏輯,以確認物件已初始化。Provide rendering logic to confirm that objects are initialized. 當物件為時,呈現預留位置 UI 專案(例如,載入訊息) nullRender placeholder UI elements (for example, a loading message) while objects are null.

FetchData 範本的元件中 Blazor , OnInitializedAsync 會覆寫為 asychronously 接收預測資料( forecasts )。In the FetchData component of the Blazor templates, OnInitializedAsync is overridden to asychronously receive forecast data (forecasts). forecasts 為時 null ,會向使用者顯示載入訊息。When forecasts is null, a loading message is displayed to the user. 在所 Task 傳回的 OnInitializedAsync 完成之後,元件會以更新的狀態重新顯示。After the Task returned by OnInitializedAsync completes, the component is rerendered with the updated state.

伺服器範本中的Pages/FetchData Blazor :Pages/FetchData.razor in the Blazor Server template:

@page "/fetchdata"
@using MyBlazorApp.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

使用 IDisposable 的元件處置Component disposal with IDisposable

如果元件會執行 IDisposable ,則會在從 UI 中移除元件時呼叫Dispose 方法If a component implements IDisposable, the Dispose method is called when the component is removed from the UI. 下列元件會使用 @implements IDisposableDispose 方法:The following component uses @implements IDisposable and the Dispose method:

@using System
@implements IDisposable

...

@code {
    public void Dispose()
    {
        ...
    }
}

注意

StateHasChanged不支援在中呼叫 DisposeCalling StateHasChanged in Dispose isn't supported. StateHasChanged可能會在卸載轉譯器的過程中叫用,因此不支援在該時間點要求 UI 更新。StateHasChanged might be invoked as part of tearing down the renderer, so requesting UI updates at that point isn't supported.

取消訂閱來自 .NET 事件的事件處理常式。Unsubscribe event handlers from .NET events. 下列 Blazor 表單範例示範如何在方法中解除掛接事件處理常式 DisposeThe following Blazor form examples show how to unhook an event handler in the Dispose method:

  • 私用欄位和 lambda 方法Private field and lambda approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(...);
    
            fieldChanged = (_, __) =>
            {
                ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • 私用方法方法Private method approach

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
    
        ...
    
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(...);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

處理錯誤Handle errors

如需在生命週期方法執行期間處理錯誤的詳細資訊,請參閱 <xref:blazor/handle-errors#lifecycle-methods> 。For information on handling errors during lifecycle method execution, see <xref:blazor/handle-errors#lifecycle-methods>.

預呈現後的具狀態重新連接Stateful reconnection after prerendering

在 Blazor 伺服器應用程式中 RenderMode ,當為時 ServerPrerendered ,元件一開始會以靜態方式呈現為頁面的一部分。In a Blazor Server app when RenderMode is ServerPrerendered, the component is initially rendered statically as part of the page. 當瀏覽器建立回到伺服器的連接後,就會再次轉譯該元件,而且該元件現在是互動式的。Once the browser establishes a connection back to the server, the component is rendered again, and the component is now interactive. 如果存在用於初始化元件的OnInitialized {Async}生命週期方法,則會執行兩次方法:If the OnInitialized{Async} lifecycle method for initializing the component is present, the method is executed twice:

  • 當元件以靜態方式資源清單時。When the component is prerendered statically.
  • 建立伺服器連接之後。After the server connection has been established.

這可能會導致在最後呈現元件時,UI 中顯示的資料有明顯的變更。This can result in a noticeable change in the data displayed in the UI when the component is finally rendered.

若要避免伺服器應用程式中的雙呈現案例 Blazor :To avoid the double-rendering scenario in a Blazor Server app:

  • 傳入識別碼,可在自動處理期間用來快取狀態,並在應用程式重新開機之後,取得狀態。Pass in an identifier that can be used to cache the state during prerendering and to retrieve the state after the app restarts.
  • 在預入期間使用識別碼來儲存元件狀態。Use the identifier during prerendering to save component state.
  • 在可呈現後使用識別碼,以取得快取的狀態。Use the identifier after prerendering to retrieve the cached state.

下列程式碼示範 WeatherForecastService 在以範本為基礎的 Blazor 伺服器應用程式中已更新,可避免雙重呈現:The following code demonstrates an updated WeatherForecastService in a template-based Blazor Server app that avoids the double rendering:

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    
    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }
    
    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = 
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

如需的詳細資訊 RenderMode ,請參閱 <xref:blazor/hosting-model-configuration#render-mode> 。For more information on the RenderMode, see <xref:blazor/hosting-model-configuration#render-mode>.

偵測應用程式何時已進行預呈現Detect when the app is prerendering



當 Blazor 伺服器應用程式進行預先處理時,某些動作(例如呼叫 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 interop 呼叫,直到建立與瀏覽器的連線之後,您可以使用OnAfterRenderAsync 元件生命週期事件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 <head> Wwwroot/index.html (Blazor WebAssembly)或Pages/_Host. cshtml (Blazor Server)的元素內提供 JavaScript 函式。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). 呼叫函式時 JSRuntimeExtensions.InvokeVoidAsync ,不會傳回值:The function is called with JSRuntimeExtensions.InvokeVoidAsync and doesn't return a value:

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

警告

上述範例只會修改檔物件模型(DOM),僅供示範之用。The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. 在大部分的情況下,不建議直接修改具有 JavaScript 的 DOM,因為 JavaScript 可能會干擾 Blazor 的變更追蹤。Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

下列元件示範如何使用 JavaScript interop 做為元件初始化邏輯的一部分,使其與可處理性相容。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. 此元件顯示可以從內部觸發轉譯更新 OnAfterRenderAsyncThe 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.InvokeAsyncElementRef 只會用於 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.

呼叫StateHasChanged以 rerender 具有從 JavaScript interop 呼叫取得之新狀態的元件。StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call. 程式碼不會建立無限迴圈,因為 StateHasChanged 只有在是時才會呼叫 infoFromJs nullThe 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 <head> Wwwroot/index.html (Blazor WebAssembly)或Pages/_Host. cshtml (Blazor Server)的元素內提供 JavaScript 函式。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 withIJSRuntime.InvokeAsync and returns a value:

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

警告

上述範例只會修改檔物件模型(DOM),僅供示範之用。The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. 在大部分的情況下,不建議直接修改具有 JavaScript 的 DOM,因為 JavaScript 可能會干擾 Blazor 的變更追蹤。Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.

可取消的背景工作Cancelable background work

元件通常會執行長時間執行的背景工作,例如進行網路呼叫( HttpClient )並與資料庫互動。Components often perform long-running background work, such as making network calls (HttpClient) and interacting with databases. 在幾種情況下,最好停止背景工作以節省系統資源。It's desirable to stop the background work to conserve system resources in several situations. 例如,當使用者離開元件時,背景非同步作業不會自動停止。For example, background asynchronous operations don't automatically stop when a user navigates away from a component.

背景工作專案可能需要取消的其他原因包括:Other reasons why background work items might require cancellation include:

  • 執行中的背景工作使用了錯誤的輸入資料或處理參數來啟動。An executing background task was started with faulty input data or processing parameters.
  • 目前執行中的背景工作專案集必須以一組新的工作專案取代。The current set of executing background work items must be replaced with a new set of work items.
  • 必須變更目前正在執行之工作的優先順序。The priority of currently executing tasks must be changed.
  • 必須關閉應用程式,才能將它重新部署到伺服器。The app has to be shut down in order to redeploy it to the server.
  • 伺服器資源會受到限制,請強制背景工作專案的重新排定。Server resources become limited, necessitating the rescheduling of background work items.

若要在元件中執行可取消的背景工作模式:To implement a cancelable background work pattern in a component:

在下例中︰In the following example:

  • await Task.Delay(5000, cts.Token);代表長時間執行的非同步背景工作。await Task.Delay(5000, cts.Token); represents long-running asynchronous background work.
  • BackgroundResourceMethod代表長時間執行的背景方法,如果在 Resource 呼叫方法之前處置,則不應該啟動。BackgroundResourceMethod represents a long-running background method that shouldn't start if the Resource is disposed before the method is called.
@implements IDisposable
@using System.Threading

<button @onclick="LongRunningWork">Trigger long running work</button>

@code {
    private Resource resource = new Resource();
    private CancellationTokenSource cts = new CancellationTokenSource();

    protected async Task LongRunningWork()
    {
        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod();
    }

    public void Dispose()
    {
        cts.Cancel();
        cts.Dispose();
        resource.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(nameof(Resource));
            }
            
            ...
        }
        
        public void Dispose()
        {
            disposed = true;
        }
    }
}