這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱 本文的 .NET 7 版本

本文說明 ASP.NET Core Razor 元件生命週期,以及如何使用生命週期事件。

元件會在 Razor 一組同步和非同步生命週期方法中處理 Razor 元件生命週期事件。 您可以在元件初始化和轉譯期間覆寫生命週期方法,以在元件中執行其他作業。

本文可簡化元件生命週期事件處理,以厘清複雜的架構邏輯。 您可能需要存取 參考來源, ComponentBase 以整合自訂事件處理與 Blazor 的生命週期事件處理。 參考來源中的程式碼批註包含未出現在本文或 API 檔中 之生命週期事件處理的其他備註。 Blazor的生命週期事件處理已隨著時間變更,而且可能會變更,而不需注意每個版本。

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱 如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

下列簡化圖表說明 Razor 元件生命週期事件處理。 與生命週期事件相關聯的 C# 方法會以本文下列各節中的範例定義。

元件生命週期事件:

  • 如果元件第一次在要求上轉譯:
    • 建立元件的 實例。
    • 執行屬性插入。 執行 SetParametersAsync
    • 呼叫 OnInitialized{Async} 。 如果傳回不完整 Task Task 則會等候 ,然後重新呈現元件。
    • 呼叫 OnParametersSet{Async} 。 如果傳回不完整 Task Task 則會等候 ,然後重新呈現元件。
    • 呈現所有同步工作並完成 Task
    • 在轉譯元件之前,生命週期事件中執行的非同步動作可能尚未完成。 如需詳細資訊,請參閱本文稍後 的處理未完成的非同步動作 一節。

      父元件會在其子元件之前轉譯,因為轉譯會決定有哪些子系存在。 如果使用同步父元件初始化,則保證會先完成父代初始化。 如果使用非同步父元件初始化,則無法判斷父元件和子元件初始化的完成順序,因為它取決於執行中的初始化程式碼。

      檔物件模型 (DOM) 事件處理:

    • 事件處理常式正在執行。
    • 如果傳回不完整 Task Task 則會等候 ,然後重新呈現元件。
    • 呈現所有同步工作並完成 Task
    • Render 生命週期:

    • 避免在元件上進行進一步的轉譯作業:
      • 第一次轉譯之後。
      • ShouldRender false 時。
      • 建置轉譯樹狀結構差異 (差異) 並轉譯元件。
      • 等候 DOM 更新。
      • 呼叫 OnAfterRender{Async}
      • 開發人員呼叫 以 StateHasChanged 產生轉譯。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件轉譯

        設定參數時 ( SetParametersAsync )

        SetParametersAsync 會設定元件在轉譯樹狀結構或路由參數中由元件的父代提供的參數。

        方法的參數 ParameterView 包含每次 SetParametersAsync 呼叫時元件的 元件參數 值集合。 藉由覆寫 SetParametersAsync 方法,開發人員程式碼可以直接與 ParameterView 的參數互動。

        的預設實作 SetParametersAsync 會使用 [Parameter] 中具有對應值的 或 [CascadingParameter] 屬性 ,設定每個屬性的值 ParameterView 。 中 ParameterView 沒有對應值的參數會保持不變。

        如果未 base.SetParametersAsync 叫用,開發人員程式碼就可以以任何方式解譯傳入參數的值。 例如,不需要將傳入參數指派給 類別的屬性。

        如果在開發人員程式碼中提供事件處理常式,請在處置時將其解除hook。 如需詳細資訊,請參閱 元件處置與 一 IDisposable IAsyncDisposable 節。

        在下列範例中,如果 剖析 的路由參數成功, ParameterView.TryGetValue 請將 參數 Param 的值指派 Param value 。 當 不是 null value ,元件會顯示值。

        雖然 路由參數比對不區分大小寫 TryGetValue 但只會比對路由範本中的區分大小寫參數名稱。 下列範例需要在 /{Param?} 路由範本中使用 ,才能取得具有 TryGetValue 的值,而不是 /{param?} 。 如果 /{param?} 在此案例中使用 , TryGetValue 則會傳 false 回 ,且 message 未設定為任一 message 字串。

        Pages/SetParamsAsync.razor :

        @page "/set-params-async/{Param?}" <p>@message</p> @code { private string message = "Not set"; [Parameter] public string? Param { get; set; } public override async Task SetParametersAsync(ParameterView parameters) if (parameters.TryGetValue<string>(nameof(Param), out var value)) if (value is null) message = "The value of 'Param' is null."; message = $"The value of 'Param' is {value}."; await base.SetParametersAsync(parameters); public string? Param { get; set; } public override async Task SetParametersAsync(ParameterView parameters) if (parameters.TryGetValue<string>(nameof(Param), out var value)) if (value is null) message = "The value of 'Param' is null."; message = $"The value of 'Param' is {value}."; await base.SetParametersAsync(parameters); public string Param { get; set; } public override async Task SetParametersAsync(ParameterView parameters) if (parameters.TryGetValue<string>(nameof(Param), out var value)) if (value is null) message = "The value of 'Param' is null."; message = $"The value of 'Param' is {value}."; await base.SetParametersAsync(parameters); public string Param { get; set; } public override async Task SetParametersAsync(ParameterView parameters) if (parameters.TryGetValue<string>(nameof(Param), out var value)) if (value is null) message = "The value of 'Param' is null."; message = $"The value of 'Param' is {value}."; await base.SetParametersAsync(parameters);

        元件初始化 ( OnInitialized{Async} )

        OnInitialized 當元件在 中 SetParametersAsync 收到其初始參數之後,就會叫用 和 OnInitializedAsync

        如果使用同步父元件初始化,則父初始化保證會在子元件初始化之前完成。 如果使用非同步父元件初始化,則無法判斷父元件和子元件初始化的完成順序,因為它取決於執行中的初始化程式碼。

        針對同步作業,請覆寫 OnInitialized

        Pages/OnInit.razor :

        @page "/on-init" <p>@message</p> @code { private string? message; protected override void OnInitialized() message = $"Initialized at {DateTime.Now}";

        若要執行非同步作業,請覆寫 OnInitializedAsync 並使用 await 運算子:

        protected override async Task OnInitializedAsync()
            await ...
        

        Blazor在伺服器上預先呈現其內容的應用程式會呼叫 OnInitializedAsync兩次

      • 一旦元件一開始以靜態方式呈現為頁面的一部分時。
      • 瀏覽器轉譯元件的第二次。
      • 若要防止開發人員程式碼在 OnInitializedAsync 預先呈現時執行兩次,請參閱 預先呈現之後的具狀態重新連線 一節。 雖然區段中的內容著重于 Blazor Server 可 SignalR 設定狀態的重新連線,但在託管應用程式中預先 Blazor WebAssembly 呈現的案例 (WebAssemblyPrerendered) 牽涉到類似的條件和方法,以避免執行開發人員程式碼兩次。 若要在預先呈現期間保留初始化程式碼執行期間的狀態,請參閱Prerender 並整合 ASP.NET Core Razor 元件

        Blazor雖然應用程式預先呈現,但無法呼叫 JavaScript (Interop) JS 等特定動作。 預先呈現時,元件可能需要以不同的方式呈現。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先呈現 一節。

        如果在開發人員程式碼中提供事件處理常式,請在處置時將其解除hook。 如需詳細資訊,請參閱元件處置與 一 IDisposableIAsyncDisposable節。

        使用 串流轉譯 搭配伺服器端轉譯 (SSR) ,以改善在 中 OnInitializedAsync 執行長時間執行非同步工作的伺服器端元件使用者體驗,以完整轉譯。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件轉譯

        設定參數之後 (OnParametersSet{Async})

        OnParametersSetOnParametersSetAsync 稱為:

      • 在 或 OnInitializedAsyncOnInitialized 初始化元件之後。

      • 當父元件重新呈現並提供時:

      • 至少有一個參數變更時,已知或基本類型不可變。
      • 複雜型別參數。 架構無法知道複雜類型參數的值是否在內部變動,因此當有一或多個複雜類型的參數時,架構一律會將參數集視為變更。
      • 如需轉譯慣例的詳細資訊,請參閱ASP.NET Core Razor 元件轉譯

        針對下列範例元件,流覽至 URL 上的元件頁面:

      • 使用 所 StartDate 收到的開始日期: /on-parameters-set/2021-03-19
      • 如果沒有開始日期,其中 StartDate 會指派目前當地時間的值: /on-parameters-set
      • 在元件路由中,您無法同時使用路由條件約束 datetime來限制 DateTime 參數,並讓參數成為選擇性。 因此,下列 OnParamsSet 元件會使用兩 @page 個指示詞來處理 URL 中提供日期區段的路由。

        Pages/OnParamsSet.razor:

        @page "/on-params-set" @page "/on-params-set/{StartDate:datetime}" <p>@message</p> @code { private string? message; [Parameter] public DateTime StartDate { get; set; } protected override void OnParametersSet() if (StartDate == default) StartDate = DateTime.Now; message = $"No start date in URL. Default value applied (StartDate: {StartDate})."; message = $"The start date in the URL was used (StartDate: {StartDate})."; StartDate = DateTime.Now; message = $"No start date in URL. Default value applied (StartDate: {StartDate})."; message = $"The start date in the URL was used (StartDate: {StartDate})."; StartDate = DateTime.Now; message = $"No start date in URL. Default value applied (StartDate: {StartDate})."; message = $"The start date in the URL was used (StartDate: {StartDate})."; StartDate = DateTime.Now; message = $"No start date in URL. Default value applied (StartDate: {StartDate})."; message = $"The start date in the URL was used (StartDate: {StartDate}).";

        套用參數和屬性值時,必須在生命週期事件期間 OnParametersSetAsync 進行非同步工作:

        protected override async Task OnParametersSetAsync()
            await ...
        

        如果在開發人員程式碼中提供事件處理常式,請在處置時將其解除hook。 如需詳細資訊,請參閱元件處置與 一 IDisposableIAsyncDisposable節。

        如需路由參數和條件約束的詳細資訊,請參閱ASP.NET Core Blazor 路由和導覽

        元件轉譯後 (OnAfterRender{Async})

        OnAfterRenderOnAfterRenderAsync 會在元件完成轉譯之後呼叫。 此時會填入元素和元件參考。 使用此階段可對轉譯的內容執行其他初始化步驟,例如 JS 與轉譯 DOM 元素互動的 Interop 呼叫。

        OnAfterRenderAsync 的參數 firstRenderOnAfterRender

      • 設定為 true 第一次轉譯元件實例。
      • 可用來確保初始化工作只會執行一次。
      • Pages/AfterRender.razor:

        @page "/after-render" @using Microsoft.Extensions.Logging @inject ILogger<AfterRender> Logger <button @onclick="LogInformation">Log information (and trigger a render)</button> @code { private string message = "Initial assigned message."; protected override void OnAfterRender(bool firstRender) Logger.LogInformation("OnAfterRender(1): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); if (firstRender) message = "Executed for the first render."; message = "Executed after the first render."; Logger.LogInformation("OnAfterRender(2): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); private void LogInformation() Logger.LogInformation("LogInformation called"); @page "/after-render" @using Microsoft.Extensions.Logging @inject ILogger<AfterRender> Logger <button @onclick="LogInformation">Log information (and trigger a render)</button> @code { private string message = "Initial assigned message."; protected override void OnAfterRender(bool firstRender) Logger.LogInformation("OnAfterRender(1): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); if (firstRender) message = "Executed for the first render."; message = "Executed after the first render."; Logger.LogInformation("OnAfterRender(2): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); private void LogInformation() Logger.LogInformation("LogInformation called"); @page "/after-render" @using Microsoft.Extensions.Logging @inject ILogger<AfterRender> Logger <button @onclick="LogInformation">Log information (and trigger a render)</button> @code { private string message = "Initial assigned message."; protected override void OnAfterRender(bool firstRender) Logger.LogInformation("OnAfterRender(1): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); if (firstRender) message = "Executed for the first render."; message = "Executed after the first render."; Logger.LogInformation("OnAfterRender(2): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); private void LogInformation() Logger.LogInformation("LogInformation called"); @page "/after-render" @using Microsoft.Extensions.Logging @inject ILogger<AfterRender> Logger <button @onclick="LogInformation">Log information (and trigger a render)</button> @code { private string message = "Initial assigned message."; protected override void OnAfterRender(bool firstRender) Logger.LogInformation("OnAfterRender(1): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); if (firstRender) message = "Executed for the first render."; message = "Executed after the first render."; Logger.LogInformation("OnAfterRender(2): firstRender: " + "{FirstRender}, message: {Message}", firstRender, message); private void LogInformation() Logger.LogInformation("LogInformation called");

        在生命週期事件期間必須立即進行轉譯之後的 OnAfterRenderAsync 非同步工作:

        protected override async Task OnAfterRenderAsync(bool firstRender)
            if (firstRender)
                await ...
        

        即使您從 傳 OnAfterRenderAsyncTask ,架構也不會在該工作完成之後,為元件排程進一步的轉譯週期。 這是為了避免無限轉譯迴圈。 這與其他生命週期方法不同,它會在傳回完成後排程進一步的 Task 轉譯週期。

        OnAfterRenderOnAfterRenderAsync不會在伺服器上的預先呈現程式期間呼叫。 在預先呈現元件之後以互動方式轉譯時,會呼叫 方法。 當應用程式預先呈現時:

      • 元件會在伺服器上執行,以在 HTTP 回應中產生一些靜態 HTML 標籤。 在這個階段, OnAfterRender 不會呼叫 和 OnAfterRenderAsync
      • 當 Blazor 腳本 (blazor.webassembly.jsblazor.server.js) 在瀏覽器中啟動時,元件會以互動式轉譯模式重新開機。 重新開機元件之後, OnAfterRenderOnAfterRenderAsync 呼叫 ,因為應用程式不再處於預先呈現階段。
      • 如果在開發人員程式碼中提供事件處理常式,請在處置時將其解除hook。 如需詳細資訊,請參閱元件處置與 一 IDisposableIAsyncDisposable節。

        狀態變更 (StateHasChanged)

        StateHasChanged 通知元件其狀態已變更。 如果適用,呼叫 StateHasChanged 會導致重新呈現元件。

        StateHasChanged 會針對方法自動呼叫 EventCallback 。 如需事件回呼的詳細資訊,請參閱ASP.NET Core Blazor 事件處理

        如需元件轉譯和何時呼叫 StateHasChanged 的詳細資訊,包括何時使用 叫用 ComponentBase.InvokeAsync 元件,請參閱ASP.NET Core Razor 元件轉譯

        在轉譯時處理不完整的非同步動作

        在轉譯元件之前,生命週期事件中執行的非同步動作可能尚未完成。 當生命週期方法執行時,物件可能會 null 或不完整地填入資料。 提供轉譯邏輯,以確認物件已初始化。 例如,當 物件為 null 時,轉譯預留位置 UI 元素 (載入訊息) 。

        FetchData 範本的 元件中 Blazor , OnInitializedAsync 會覆寫為非同步接收預測資料 (forecasts) 。 當 為 nullforecasts ,載入訊息會顯示給使用者。 Task在 傳回的 OnInitializedAsync 完成之後,元件會以更新的狀態重新呈現。

        Pages/FetchData.razor 在範本中 Blazor Server :

        @page "/fetchdata" @using BlazorSample.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> <table class="table"> <!-- forecast data in table element content --> </table> @code { private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now)); protected override async Task OnInitializedAsync() forecasts = await ForecastService.GetForecastAsync(DateTime.Now); protected override async Task OnInitializedAsync() forecasts = await ForecastService.GetForecastAsync(DateTime.Now); protected override async Task OnInitializedAsync() forecasts = await ForecastService.GetForecastAsync(DateTime.Now);

        如需在生命週期方法執行期間處理錯誤的資訊,請參閱處理 ASP.NET Core Blazor 應用程式中的錯誤

        預先呈現之後的具狀態重新連線

        Blazor Server在 當 為 ServerPrerenderedRenderMode ,元件一開始會以靜態方式呈現為頁面的一部分。 一旦瀏覽器建立 SignalR 與伺服器的連線,元件就會 再次 轉譯且互動式。 OnInitialized{Async}如果元件初始化的生命週期方法存在,則會執行方法兩次

      • 以靜態方式預先呈現元件時。
      • 建立伺服器連接之後。
      • 當元件最終轉譯時,這可能會導致 UI 中顯示的資料有明顯的變更。 若要避免在應用程式中執行 Blazor Server 這個雙重轉譯行為,請在預先呈現期間傳入識別碼來快取狀態,並在預先呈現之後擷取狀態。

        下列程式碼示範範本型 Blazor Server 應用程式中的更新 WeatherForecastService ,以避免雙重轉譯。 在下列範例中,等候 Delayawait Task.Delay(...) () 會先模擬短暫的延遲,再從 GetForecastAsync 方法傳回資料。

        WeatherForecastService.cs:

        using Microsoft.Extensions.Caching.Memory; 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(); 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(); using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using BlazorSample.Shared; 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(); using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using BlazorSample.Shared; 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 ,請參閱ASP.NET Core BlazorSignalR 指引

        雖然本節中的內容著重于 Blazor Server 可設定狀態 SignalR 的重新連線,但在託管應用程式中預先 Blazor WebAssembly 呈現的案例 (WebAssemblyPrerendered) 牽涉到類似的條件和方法,以避免執行開發人員程式碼兩次。 若要在預先呈現期間保留初始化程式碼執行期間的狀態,請參閱Prerender 並整合 ASP.NET Core Razor 元件

        使用 JavaScript Interop 預先呈現

        本節適用于 Blazor Server 預先呈現 Razor 元件的 裝載 Blazor WebAssembly 應用程式。 預先呈現涵蓋在Prerender 中,並整合 ASP.NET Core Razor 元件

        雖然應用程式預先呈現,但無法呼叫 JavaScript () JS 等特定動作。

        在下列範例中,函 setElementText1 式會放在 元素內 <head> 。 函式會使用 JSRuntimeExtensions.InvokeVoidAsync 呼叫,而且不會傳回值。

        如需位置和生產應用程式建議的 JS 一般指引,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS Interop)

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

        上述範例會直接修改 Document Object Model (DOM) 僅供示範之用。 在大部分情況下不建議直接修改 DOM JS ,因為 JS 可能會干擾 Blazor 變更追蹤。 如需詳細資訊,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS interop)

        OnAfterRender{Async}伺服器上的預先呈現程式期間,不會呼叫生命週期事件。 OnAfterRender{Async}覆寫 方法以延遲 JS Interop 呼叫,直到在預先呈現元件之後,並在用戶端上以互動方式呈現為止。

        Pages/PrerenderedInterop1.razor:

        @page "/prerendered-interop-1" @using Microsoft.JSInterop @inject IJSRuntime JS <div @ref="divElement">Text during render</div> @code { private ElementReference divElement; protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) await JS.InvokeVoidAsync( "setElementText1", divElement, "Text after render");

        上述範例會使用全域方法來叫用用戶端。 如需生產應用程式中更好的方法,請參閱 JavaScript 模組中的 JavaScript 隔離

        export setElementText1 = (element, text) => element.innerText = text;
        

        下列元件示範如何使用 JS Interop 作為元件初始化邏輯的一部分,以與預先呈現相容的方式。 元件顯示可以從 內部 OnAfterRenderAsync 觸發轉譯更新。 開發人員必須小心,以避免在此案例中建立無限迴圈。

        在下列範例中,函 setElementText2 式會放在 元素內 <head> 。 函式會使用 呼叫 IJSRuntime.InvokeAsync ,並傳回值。

        如需位置和生產應用程式建議的 JS 一般指引,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS Interop)

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

        上述範例會直接修改 Document Object Model (DOM) 僅供示範之用。 在大部分情況下不建議直接修改 DOM JS ,因為 JS 可能會干擾 Blazor 變更追蹤。 如需詳細資訊,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS interop)

        呼叫 時 JSRuntime.InvokeAsyncElementReference 只會在 和 之前的任何生命週期方法中使用 OnAfterRenderAsync ,因為在轉譯元件之後,沒有 JS 元素。

        StateHasChanged會呼叫 以重新呈現元件,其中包含從 JS interop 呼叫 (取得的新狀態,如需詳細資訊,請參閱ASP.NET Core Razor 元件轉譯) 。 程式碼不會建立無限迴圈,因為 StateHasChanged 只有在 為 nulldata 才會呼叫 。

        Pages/PrerenderedInterop2.razor:

        @page "/prerendered-interop-2" @using Microsoft.AspNetCore.Components @using Microsoft.JSInterop @inject IJSRuntime JS Get value via JS interop call: <strong id="val-get-by-interop">@(data ?? "No value yet")</strong> Set value via JS interop call: <div id="val-set-by-interop" @ref="divElement"></div> @code { private string? data; private ElementReference divElement; protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender && data == null) data = await JS.InvokeAsync<string>( "setElementText2", divElement, "Hello from interop call!"); StateHasChanged();

        上述範例會使用全域方法來叫用用戶端。 如需生產應用程式中更好的方法,請參閱 JavaScript 模組中的 JavaScript 隔離

        export setElementText2 = (element, text) => {
          element.innerText = text;
          return text;
        

        搭配 和 的 IDisposable 元件處置 IAsyncDisposable

        如果元件實作 IDisposableIAsyncDisposable 或兩者,當元件從 UI 移除時,架構會呼叫 Unmanaged 資源處置。 您可以隨時進行處置,包括 元件初始化期間。

        元件不應該同時實 IDisposable 作 和 IAsyncDisposable 。 如果兩者都已實作,架構只會執行非同步多載。

        開發人員程式碼必須確保 IAsyncDisposable 實作不需要很長的時間才能完成。

        JavaScript Interop 物件參考的處置

        JavaScript () JS Interop 文章中的範例會示範典型的物件處置模式:

      • 從 .NET 呼叫 JS 時,如從 ASP.NET Core Blazor 中從 .NET 方法呼叫 JavaScript 函式中所述,處置從 .NET 或 從 JS 中建立的任何函 IJSInProcessObjectReference//IJSObjectReferenceJSObjectReference 式,以避免記憶體流失。 JS

      • 從 JS 呼叫 .NET 時,如從 ASP.NET Core Blazor 中的 JavaScript 函式呼叫 .NET 方法中所述,請處置從 .NET 或從 JS 中建立 DotNetObjectReference 的 ,以避免洩漏 .NET 記憶體。

        JS Interop 物件參考會實作為對應索引鍵,由建立參考之 Interop 呼叫端的 JS 識別碼進行索引鍵。 從 .NET 或 JS 側邊起始物件處置時, Blazor 請從對應中移除專案,只要物件沒有其他強式參考存在,就可以垃圾收集物件。

        至少,請一律處置在 .NET 端建立的物件,以避免流失 .NET Managed 記憶體。

        檔物件模型 (DOM) 元件處置期間的清除工作

        如需詳細資訊,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS interop)

        如需中斷線路時應用程式中的指引 JSDisconnectedExceptionBlazor Server ,請參閱ASP.NET Core Blazor JavaScript 互通性 (JS interop) 。 如需一般 JavaScript Interop 錯誤處理指引,請參閱處理 ASP.NET Core Blazor 應用程式中的錯誤中的JavaScript Interop一節。

        同步 IDisposable

        針對同步處置工作,請使用 IDisposable.Dispose

        下列元件:

      • IDisposable使用 指示詞實作 @implementsRazor 。
      • obj處置 ,這是實 IDisposable 作 的 Unmanaged 型別。
      • 因為是在生命週期方法中建立的,所以會執行 obj null 檢查, (未顯示) 。
      • @implements IDisposable
        @code {
            public void Dispose()
                obj?.Dispose();
        

        如果單一物件需要處置,則呼叫 時 Dispose ,可以使用 Lambda 來處置物件。 下列範例會出現在ASP.NET Core Razor 元件轉譯一文中,並示範如何使用 Lambda 運算式來處置 Timer

        Pages/CounterWithTimerDisposal1.razor:

        @page "/counter-with-timer-disposal-1" @using System.Timers @implements IDisposable <h1>Counter with <code>Timer</code> disposal</h1> <p>Current count: @currentCount</p> @code { private int currentCount = 0; private Timer timer = new(1000); protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer.Dispose(); protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer.Dispose(); protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer.Dispose(); protected override void OnInitialized() timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer.Dispose();

        在上述範例中,對 的呼叫 StateHasChanged 會由 呼叫 ComponentBase.InvokeAsync 包裝,因為回呼是在 同步處理內容外部 Blazor 叫用。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件轉譯

        如果在生命週期方法中建立 物件,例如 OnInitialized/OnInitializedAsync ,請在呼叫 Dispose 之前檢查 null

        Pages/CounterWithTimerDisposal2.razor:

        @page "/counter-with-timer-disposal-2" @using System.Timers @implements IDisposable <h1>Counter with <code>Timer</code> disposal</h1> <p>Current count: @currentCount</p> @code { private int currentCount = 0; private Timer? timer; protected override void OnInitialized() timer = new Timer(1000); timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer?.Dispose(); timer = new Timer(1000); timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer?.Dispose(); timer = new Timer(1000); timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer?.Dispose(); timer = new Timer(1000); timer.Elapsed += (sender, eventArgs) => OnTimerCallback(); timer.Start(); private void OnTimerCallback() _ = InvokeAsync(() => currentCount++; StateHasChanged(); public void Dispose() => timer?.Dispose();

        如需詳細資訊,請參閱

      • 清除非受控資源 (.NET 檔)
      • Null 條件運算子 ?. 和 ?[]
      • 非同步 IAsyncDisposable

        針對非同步處置工作,請使用 IAsyncDisposable.DisposeAsync

        下列元件:

      • IAsyncDisposable使用 指示詞實作 @implementsRazor 。
      • obj處置 ,這是實 IAsyncDisposable 作 的 Unmanaged 型別。
      • 因為是在生命週期方法中建立的,所以會執行 obj null 檢查, (未顯示) 。
      • @implements IAsyncDisposable
        @code {
            public async ValueTask DisposeAsync()
                if (obj is not null)
                    await obj.DisposeAsync();
        

        如需詳細資訊,請參閱

      • 清除非受控資源 (.NET 檔)
      • Null 條件運算子 ?. 和 ?[]
      • null指派給已處置的物件

        通常,呼叫 Dispose/DisposeAsync 之後就不需要指派 null 給處置的物件。 指派 null 的罕見案例包括:

      • 如果物件的型別實作不佳,而且無法容許重複呼叫 Dispose/DisposeAsync ,請在處置之後指派 null ,以正常略過對 Dispose/DisposeAsync 的進一步呼叫。
      • 如果長時間存留的進程繼續保存已處置物件的參考,指派 null 可讓 垃圾收集行程 釋放物件,同時保存該物件的參考。
      • 這些是不尋常的案例。 對於正確實作且正常運作的物件,指派給已處置的物件沒有點 null 。 在必須指派 null 物件的罕見情況下,建議您記錄原因,並尋求解決方案,以避免需要指派 null

        StateHasChanged

        不支援在 中 Dispose 呼叫 StateHasChangedStateHasChanged 可能會在終止轉譯器時叫用,因此不支援在該時間點要求 UI 更新。

        事件處理常式

        一律從 .NET 事件取消訂閱事件處理常式。 下列Blazor 表單範例示範如何在 方法中 Dispose 取消訂閱事件處理常式:

      • 私人欄位和 Lambda 方法

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

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

        如需詳細資訊,請參閱搭配 IDisposableIAsyncDisposable 的元件處置一節。

        匿名函式、方法和運算式

        使用 匿名函式、方法或運算式時,不需要實 IDisposable 作和取消訂閱委派。 不過,當 公開事件的物件超過註冊委派的元件存留期時,無法取消訂閱委派是個問題。 發生這種情況時,記憶體流失會產生,因為已註冊的委派會讓原始物件保持運作。 因此,只有在您知道事件委派快速處置時,才使用下列方法。 當不確定需要處置的物件存留期時,請訂閱委派方法,並如先前範例所示正確地處置委派。

      • 匿名 Lambda 方法 (明確處置不需要) :

        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
            formInvalid = !editContext.Validate();
            StateHasChanged();
        protected override void OnInitialized()
            editContext = new(starship);
            editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
        
      • 匿名 Lambda 運算式方法 (明確處置不需要) :

        private ValidationMessageStore? messageStore;
        [CascadingParameter]
        private EditContext? CurrentEditContext { get; set; }
        protected override void OnInitialized()
            messageStore = new(CurrentEditContext);
            CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
            CurrentEditContext.OnFieldChanged += (s, e) => 
                messageStore.Clear(e.FieldIdentifier);
        

        具有匿名 Lambda 運算式的上述程式碼完整範例會出現在ASP.NET Core Blazor 表單和輸入元件一文中。

        如需詳細資訊,請參閱 清除 Unmanaged 資源 及其後續 Dispose 實作 和 DisposeAsync 方法的主題。

        可取消的背景工作

        元件通常會執行長時間執行的背景工作,例如 (網路呼叫) HttpClient 並與資料庫互動。 建議您停止背景工作,以在數種情況下節省系統資源。 例如,當使用者離開元件時,背景非同步作業不會自動停止。

        背景工作專案可能需要取消的其他原因包括:

      • 執行中的背景工作是以錯誤的輸入資料或處理參數啟動。
      • 目前的執行背景工作專案集必須取代為一組新的工作專案。
      • 目前執行中工作的優先順序必須變更。
      • 應用程式必須關閉,才能重新部署伺服器。
      • 伺服器資源變得有限,需要重新排程背景工作專案。
      • 若要在元件中實作可取消的背景工作模式:

      • CancellationTokenSource使用 和 CancellationToken
      • 若要 處置元件 ,而且在任何時間點取消時,需要手動取消權杖,呼叫 CancellationTokenSource.Cancel 以表示應該取消背景工作。
      • 在非同步呼叫傳回之後,請在權杖上呼叫 ThrowIfCancellationRequested
      • 在下例中︰

      • await Task.Delay(5000, cts.Token); 代表長時間執行的非同步背景工作。
      • BackgroundResourceMethod 表示長時間執行的背景方法,如果 已在呼叫 方法之前處置 , Resource 則不應該啟動。
      • Pages/BackgroundWork.razor:

        @page "/background-work" @using System.Threading @using Microsoft.Extensions.Logging @implements IDisposable @inject ILogger<BackgroundWork> Logger <button @onclick="LongRunningWork">Trigger long running work</button> <button @onclick="Dispose">Trigger Disposal</button> @code { private Resource resource = new(); private CancellationTokenSource cts = new(); protected async Task LongRunningWork() Logger.LogInformation("Long running work started"); await Task.Delay(5000, cts.Token); cts.Token.ThrowIfCancellationRequested(); resource.BackgroundResourceMethod(Logger); public void Dispose() Logger.LogInformation("Executing Dispose"); cts.Cancel(); cts.Dispose(); resource?.Dispose(); private class Resource : IDisposable private bool disposed; public void BackgroundResourceMethod(ILogger<BackgroundWork> logger) logger.LogInformation("BackgroundResourceMethod: Start method"); if (disposed) logger.LogInformation("BackgroundResourceMethod: Disposed"); throw new ObjectDisposedException(nameof(Resource)); // Take action on the Resource logger.LogInformation("BackgroundResourceMethod: Action on Resource"); public void Dispose() disposed = true; @using Microsoft.Extensions.Logging @implements IDisposable @inject ILogger<BackgroundWork> Logger <button @onclick="LongRunningWork">Trigger long running work</button> <button @onclick="Dispose">Trigger Disposal</button> @code { private Resource resource = new(); private CancellationTokenSource cts = new(); protected async Task LongRunningWork() Logger.LogInformation("Long running work started"); await Task.Delay(5000, cts.Token); cts.Token.ThrowIfCancellationRequested(); resource.BackgroundResourceMethod(Logger); public void Dispose() Logger.LogInformation("Executing Dispose"); cts.Cancel(); cts.Dispose(); resource?.Dispose(); private class Resource : IDisposable private bool disposed; public void BackgroundResourceMethod(ILogger<BackgroundWork> logger) logger.LogInformation("BackgroundResourceMethod: Start method"); if (disposed) logger.LogInformation("BackgroundResourceMethod: Disposed"); throw new ObjectDisposedException(nameof(Resource)); // Take action on the Resource logger.LogInformation("BackgroundResourceMethod: Action on Resource"); public void Dispose() disposed = true; @using Microsoft.Extensions.Logging @implements IDisposable @inject ILogger<BackgroundWork> Logger <button @onclick="LongRunningWork">Trigger long running work</button> <button @onclick="Dispose">Trigger Disposal</button> @code { private Resource resource = new(); private CancellationTokenSource cts = new(); protected async Task LongRunningWork() Logger.LogInformation("Long running work started"); await Task.Delay(5000, cts.Token); cts.Token.ThrowIfCancellationRequested(); resource.BackgroundResourceMethod(Logger); public void Dispose() Logger.LogInformation("Executing Dispose"); cts.Cancel(); cts.Dispose(); resource?.Dispose(); private class Resource : IDisposable private bool disposed; public void BackgroundResourceMethod(ILogger<BackgroundWork> logger) logger.LogInformation("BackgroundResourceMethod: Start method"); if (disposed) logger.LogInformation("BackgroundResourceMethod: Disposed"); throw new ObjectDisposedException(nameof(Resource)); // Take action on the Resource logger.LogInformation("BackgroundResourceMethod: Action on Resource"); public void Dispose() disposed = true; @using Microsoft.Extensions.Logging @implements IDisposable @inject ILogger<BackgroundWork> Logger <button @onclick="LongRunningWork">Trigger long running work</button> <button @onclick="Dispose">Trigger Disposal</button> @code { private Resource resource = new Resource(); private CancellationTokenSource cts = new CancellationTokenSource(); protected async Task LongRunningWork() Logger.LogInformation("Long running work started"); await Task.Delay(5000, cts.Token); cts.Token.ThrowIfCancellationRequested(); resource.BackgroundResourceMethod(Logger); public void Dispose() Logger.LogInformation("Executing Dispose"); cts.Cancel(); cts.Dispose(); resource?.Dispose(); private class Resource : IDisposable private bool disposed; public void BackgroundResourceMethod(ILogger<BackgroundWork> logger) logger.LogInformation("BackgroundResourceMethod: Start method"); if (disposed) logger.LogInformation("BackgroundResourceMethod: Disposed"); throw new ObjectDisposedException(nameof(Resource)); // Take action on the Resource logger.LogInformation("BackgroundResourceMethod: Action on Resource"); public void Dispose() disposed = true;

        Blazor Server 重新線上活動

        本文涵蓋的元件生命週期事件會與重新連線事件處理常式分開 Blazor Server運作。 Blazor Server當應用程式失去用戶端 SignalR 的連線時,只會中斷 UI 更新。 重新建立連線時,會繼續 UI 更新。 如需線路處理程式事件和組態的詳細資訊,請參閱ASP.NET Core BlazorSignalR 指引

  •