1. 什麼是生命周期?
在 Blazor 或 ASP.NET Core 中,服務的生命周期 指的是 一個服務實例的存活時間,也就是說:
- 從服務創建到釋放期間,它可以被重複使用多少次,或者被誰共享?
當我們註冊一個服務時(例如:builder.Services.AddScoped<MyService>()
),我們需要告訴系統:
- 這個服務應該存活多久?
- 應該在哪些情況下重複使用?
2. 三種常見的生命周期
(1)Singleton
(單例)
- 定義:全應用 只創建一個實例,所有地方都使用同一個實例。
- 什麼時候用?
- 當服務是無狀態的(比如全局設置、日誌記錄器)。
- 或者服務需要在整個應用中共享狀態。
範例:
builder.Services.AddSingleton<MyService>();
- 效果:不論在哪個組件中注入
MyService
,它們都會使用同一個實例。 - 應用場景:配置信息、全局的狀態管理。
(2)Scoped
(範圍)
- 定義:
- Blazor Server 模式中,
Scoped
表示服務的實例在 同一個用戶的連接 中是共享的。 - Blazor WebAssembly 模式中,
Scoped
的行為與Singleton
一樣,整個應用中只有一個實例。
- Blazor Server 模式中,
- 什麼時候用?
- 當需要針對每個用戶或每個連接保持獨立的狀態(如用戶的登錄信息、會話數據)。
範例:
builder.Services.AddScoped<MyService>();
- 效果:
- 對於同一個用戶,該用戶的所有組件會共享同一個服務實例。
- 不同用戶之間,服務的實例是分開的。
(3)Transient
(瞬態)
- 定義:每次請求 都會創建一個新的服務實例,完全不共享。
- 什麼時候用?
- 當服務只執行短期操作,並且不需要共享狀態時。
- 避免狀態污染,例如處理瞬時數據或計算操作。
範例:
builder.Services.AddTransient<MyService>();
- 效果:
- 每次注入
MyService
,系統都會創建一個新的實例。 - 不同組件、不同請求使用的都是全新的一個
MyService
。
- 每次注入
3. 具體例子解釋
假設我們有一個服務 AppState
,用於保存當前用戶的名字:
服務代碼:AppState
public class AppState
{
public string CurrentUser { get; set; } = "Guest";
}
情境 1:Singleton
在 Program.cs
中註冊:
builder.Services.AddSingleton<AppState>();
使用效果:
- 所有用戶 共用同一個
AppState
實例。 - 如果某個用戶更改了
AppState.CurrentUser
,所有用戶都會看到相同的更改。
範例代碼:
組件 1(改變狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
<button @onclick="ChangeUser">Set User as Alice</button>
@code {
private void ChangeUser()
{
AppState.CurrentUser = "Alice";
}
}
組件 2(讀取狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
結果:改變在所有地方生效。
情境 2:Scoped
在 Program.cs
中註冊:
builder.Services.AddScoped<AppState>();
使用效果(Blazor Server):
- 每個用戶 的
AppState
實例是獨立的。 - 同一個用戶的多個組件共享同一個實例。
範例代碼:
組件 1(改變狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
<button @onclick="ChangeUser">Set User as Alice</button>
@code {
private void ChangeUser()
{
AppState.CurrentUser = "Alice";
}
}
組件 2(讀取狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
結果:
- 同一個用戶看到的名字會改變。
- 不同用戶之間的名字彼此獨立。
情境 3:Transient
在 Program.cs
中註冊:
builder.Services.AddTransient<AppState>();
使用效果:
- 每次注入 都是全新的實例。
- 不同的組件和請求,無法共享狀態。
範例代碼:
組件 1(改變狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
<button @onclick="ChangeUser">Set User as Alice</button>
@code {
private void ChangeUser()
{
AppState.CurrentUser = "Alice";
}
}
組件 2(讀取狀態):
@inject AppState AppState
<p>Current User: @AppState.CurrentUser</p>
結果:
- 即使在組件 1 設置了
AppState.CurrentUser
,組件 2 中仍然顯示默認值Guest
。
4. 小結
生命周期 | 作用範圍 | 適用場景 |
---|---|---|
Singleton |
全應用共享一個實例。 | 配置信息、全局狀態、日誌記錄。 |
Scoped |
每個用戶的連接範圍內共享一個實例(Blazor Server);等同於 Singleton (Blazor WebAssembly)。 |
用戶相關的狀態管理,如登錄會話。 |
Transient |
每次請求創建新的實例,不共享。 | 短期操作,如輕量級計算。 |
- 核心區別 是服務的作用範圍和共享行為。
- 根據應用場景選擇合適的生命周期,可以提高代碼的性能和可維護性。
5. 補充
當你在 .razor
組件裡直接 聲明並實例化一個類 時,這個類的生命周期是 與該組件的生命周期一致。也就是說,這個類的實例在組件的 每次加載或重新渲染 時會被重新創建,並在組件被銷毀時一起銷毀。