本文和此節點中的其他文章說明在使用者使用應用程式和跨瀏覽器會話時維護使用者數據(狀態)的常見方法,包括伺服器預先呈現期間。
應用程式開發期間 Blazor 的典型需求是跨元件共享狀態:
- 父對子元件:使用參數將狀態傳遞至子元件。
- 子對父系:子元件可讓數據系結至其狀態,或透過回呼提供狀態。
- 父代對子系:父系共享狀態,其所有子系都會使用級聯值。
- 全應用程式:使用已設定的應用程式狀態服務,在整個應用程式中共享狀態。
- 每個線路:使用範圍應用程式狀態服務針對特定線路共享狀態狀態。
持續性狀態可能需要在頁面重新整理、繼續線路和預先呈現時存留。 狀態通常需要集中管理、追蹤和測試。 保存狀態的位置和技術是高度可變的。
Blazor 不提供全面、有意見的狀態管理。 與 Flux、Redux 和 MobX 等順暢運作 Blazor的第三方狀態容器產品和服務幾乎滿足任何應用程式需求。
本文的其餘部分將討論任何類型的應用程式一 Blazor 般狀態管理原則。
使用 URL 的狀態管理
若為代表瀏覽狀態的暫時性資料,請將資料模型化為 URL 的一部分。 在 URL 中建立模型的使用者狀態範例包括:
- 已檢視實體的識別碼。
- 分頁方格中的目前頁碼。
會保留瀏覽器網址列的內容:
- 如果使用者手動重新載入頁面。
- 僅限伺服器端案例:如果網頁伺服器變成無法使用,且使用者被迫重載頁面,才能連線到不同的伺服器。
如需使用 @page 指示詞定義 URL 模式的資訊,請參閱 ASP.NET Core Blazor 路由和導覽。
記憶體內部狀態容器服務
巢狀元件通常會使用鏈結繫結來繫結資料,如 ASP.NET Core Blazor 資料繫結中所述。 巢狀和未巢狀的元件可以使用已註冊的記憶體內部狀態容器來共用資料的存取權。 自訂狀態容器類別可以使用可指派的 Action,在應用程式的不同部分通知元件狀態變更。 在以下範例中:
- 一組元件會使用狀態容器來追蹤屬性。
- 下列範例中的一個元件是嵌套於另一個元件中,但此方法無需嵌套即可運作。
這很重要
本節中的範例示範如何建立記憶體內部狀態容器服務、註冊服務,以及在元件中使用服務。 此範例不會在沒有進一步開發的情況下保存資料。 為了持續儲存資料,狀態容器必須採用在清除瀏覽器記憶體時存留的基礎儲存機制。 這可以透過 localStorage/sessionStorage 或一些其他技術來完成。
StateContainer.cs:
public class StateContainer
{
private string? savedString;
public string Property
{
get => savedString ?? string.Empty;
set
{
savedString = value;
NotifyStateChanged();
}
}
public event Action? OnChange;
private void NotifyStateChanged() => OnChange?.Invoke();
}
用戶端應用程式 (Program 檔案):
builder.Services.AddSingleton<StateContainer>();
伺服器端應用程式 (Program 檔案、.NET 6 或更新版本中的 ASP.NET Core):
builder.Services.AddScoped<StateContainer>();
伺服器端應用程式 (Startup.ConfigureServices 的 Startup.cs,通常是在 .NET 6 或更早版本):
services.AddScoped<StateContainer>();
Shared/Nested.razor:
@implements IDisposable
@inject StateContainer StateContainer
<h2>Nested component</h2>
<p>Nested component Property: <b>@StateContainer.Property</b></p>
<p>
<button @onclick="ChangePropertyValue">
Change the Property from the Nested component
</button>
</p>
@code {
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
private void ChangePropertyValue()
{
StateContainer.Property =
$"New value set in the Nested component: {DateTime.Now}";
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
StateContainerExample.razor:
@page "/state-container-example"
@implements IDisposable
@inject StateContainer StateContainer
<h1>State Container Example component</h1>
<p>State Container component Property: <b>@StateContainer.Property</b></p>
<p>
<button @onclick="ChangePropertyValue">
Change the Property from the State Container Example component
</button>
</p>
<Nested />
@code {
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
private void ChangePropertyValue()
{
StateContainer.Property = "New value set in the State " +
$"Container Example component: {DateTime.Now}";
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
上述元件會實作 IDisposable,在 OnChange 方法中會取消訂閱 Dispose 委派,而這些方法是在元件被處置時由架構呼叫的。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件處置。
層疊值與參數
使用 級聯值和參數 ,藉由將數據從上階 Razor 元件流向子代元件來管理狀態:
- 若要在多個元件之間取用狀態。
- 如果只有一個最上層狀態物件需要持久化。
具有 CascadingValueSource<TValue> 的根層級階級化值允許 Razor 元件訂閱者接收已變更的階級化值通知。 如需詳細資訊和運作範例,請參閱 NotifyingDalek中的 Blazor 範例。
支援外部同步處理內容的狀態 Blazor修改
當您使用自定義狀態管理服務時,希望允許來自 Blazor同步處理內容外部的狀態修改(例如,從定時器或背景服務中),所有使用的元件都必須將 StateHasChanged 呼叫封裝在 ComponentBase.InvokeAsync中。 這可確保在渲染器的同步上下文中處理變更通知。
當狀態管理服務在StateHasChanged的同步處理上下文中未呼叫Blazor時,會發生以下錯誤:
System.InvalidOperationException:「目前的執行緒與 Dispatcher 沒有關聯。 在觸發渲染或元件狀態時,使用 InvokeAsync() 將執行切換至 Dispatcher。
如需如何解決此錯誤的詳細資訊和範例,請參閱 ASP.NET Core Razor 元件轉譯。