Blazor雖然應用程式預先呈現,但無法呼叫 JavaScript (Interop) JS 等特定動作。 預先呈現時,元件可能需要以不同的方式呈現。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先呈現 一節。
@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。 如需詳細資訊,請參閱元件處置與 一 IDisposable
IAsyncDisposable
節。
如需路由參數和條件約束的詳細資訊,請參閱ASP.NET Core Blazor 路由和導覽。
元件轉譯後 (OnAfterRender{Async}
)
OnAfterRender 和 OnAfterRenderAsync 會在元件完成轉譯之後呼叫。 此時會填入元素和元件參考。 使用此階段可對轉譯的內容執行其他初始化步驟,例如 JS 與轉譯 DOM 元素互動的 Interop 呼叫。
和 OnAfterRenderAsync 的參數 firstRender
OnAfterRender :
設定為 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 ...
即使您從 傳 OnAfterRenderAsync 回 Task ,架構也不會在該工作完成之後,為元件排程進一步的轉譯週期。 這是為了避免無限轉譯迴圈。 這與其他生命週期方法不同,它會在傳回完成後排程進一步的 Task 轉譯週期。
OnAfterRender和 OnAfterRenderAsync不會在伺服器上的預先呈現程式期間呼叫。 在預先呈現元件之後以互動方式轉譯時,會呼叫 方法。 當應用程式預先呈現時:
元件會在伺服器上執行,以在 HTTP 回應中產生一些靜態 HTML 標籤。 在這個階段, OnAfterRender 不會呼叫 和 OnAfterRenderAsync 。
當 Blazor 腳本 (blazor.webassembly.js
或 blazor.server.js
) 在瀏覽器中啟動時,元件會以互動式轉譯模式重新開機。 重新開機元件之後, OnAfterRender會OnAfterRenderAsync 呼叫 ,因為應用程式不再處於預先呈現階段。
如果在開發人員程式碼中提供事件處理常式,請在處置時將其解除hook。 如需詳細資訊,請參閱元件處置與 一 IDisposable
IAsyncDisposable
節。
狀態變更 (StateHasChanged
)
StateHasChanged 通知元件其狀態已變更。 如果適用,呼叫 StateHasChanged 會導致重新呈現元件。
StateHasChanged 會針對方法自動呼叫 EventCallback 。 如需事件回呼的詳細資訊,請參閱ASP.NET Core Blazor 事件處理。
如需元件轉譯和何時呼叫 StateHasChanged 的詳細資訊,包括何時使用 叫用 ComponentBase.InvokeAsync 元件,請參閱ASP.NET Core Razor 元件轉譯。
在轉譯時處理不完整的非同步動作
在轉譯元件之前,生命週期事件中執行的非同步動作可能尚未完成。 當生命週期方法執行時,物件可能會 null
或不完整地填入資料。 提供轉譯邏輯,以確認物件已初始化。 例如,當 物件為 null
時,轉譯預留位置 UI 元素 (載入訊息) 。
在 FetchData
範本的 元件中 Blazor , OnInitializedAsync 會覆寫為非同步接收預測資料 (forecasts
) 。 當 為 null
時 forecasts
,載入訊息會顯示給使用者。 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在 當 為 ServerPrerendered 時 RenderMode ,元件一開始會以靜態方式呈現為頁面的一部分。 一旦瀏覽器建立 SignalR 與伺服器的連線,元件就會 再次 轉譯且互動式。 OnInitialized{Async}
如果元件初始化的生命週期方法存在,則會執行方法兩次:
以靜態方式預先呈現元件時。
建立伺服器連接之後。
當元件最終轉譯時,這可能會導致 UI 中顯示的資料有明顯的變更。 若要避免在應用程式中執行 Blazor Server 這個雙重轉譯行為,請在預先呈現期間傳入識別碼來快取狀態,並在預先呈現之後擷取狀態。
下列程式碼示範範本型 Blazor Server 應用程式中的更新 WeatherForecastService
,以避免雙重轉譯。 在下列範例中,等候 Delay 的 await 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.InvokeAsync , ElementReference 只會在 和 之前的任何生命週期方法中使用 OnAfterRenderAsync ,因為在轉譯元件之後,沒有 JS 元素。
StateHasChanged
會呼叫 以重新呈現元件,其中包含從 JS interop 呼叫 (取得的新狀態,如需詳細資訊,請參閱ASP.NET Core Razor 元件轉譯) 。 程式碼不會建立無限迴圈,因為 StateHasChanged
只有在 為 null
時 data
才會呼叫 。
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
如果元件實作 IDisposable 、 IAsyncDisposable 或兩者,當元件從 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使用 指示詞實作 @implements
Razor 。
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使用 指示詞實作 @implements
Razor 。
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
呼叫 StateHasChanged 。 StateHasChanged 可能會在終止轉譯器時叫用,因此不支援在該時間點要求 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;
如需詳細資訊,請參閱搭配 IDisposable
和 IAsyncDisposable
的元件處置一節。
匿名函式、方法和運算式
使用 匿名函式、方法或運算式時,不需要實 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 指引。