目錄Blazor 組件基礎路由導航參數組件參數路由參數生命周期事件狀態更改組件事件 Blazor 組件 基礎 新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項: 客戶端組件 (Auto/WebAssembly): 最終解決方案裡面會有兩個項目:伺服器 ...
目錄
Blazor 組件
基礎
新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項:
客戶端組件 (Auto/WebAssembly):
最終解決方案裡面會有兩個項目:伺服器端項目、客戶端項目,組件按存放項目的不同可以分為以下兩種組件:
-
伺服器端組件:
- 主要用於伺服器端渲染(SSR)
- 被放置在伺服器端項目中
- 適用於不需要實時交互或複雜用戶交互的場景
-
客戶端組件 (Auto/WebAssembly):
- 組件位於客戶端項目內
- 使用 WebAssembly 技術進行編譯,能夠直接與瀏覽器交互
- 適合需要交互性和實時更新的應用場景
- 使用 SignalR 可以實現實時通信,從而增強組件的功能性
兩種組件選擇原則如下:
- 如果組件不需要交互性,將其作為伺服器端渲染的組件。
- 如果組件需要交互性(例如響應用戶的輸入、實時數據更新等),則應該考慮將其作為客戶端組件,可以利用 SignalR 提供的實時通信功能。
在客戶端項目中新建一個 Demo 組件:
<!-- 選擇 Auto 或 WebAssembly ,否則無法交互 -->
@rendermode InteractiveAuto
<h3>Demo</h3>
<!-- 文本不為空時才顯示標簽 -->
@if (textInfo is not null)
{
<h4>Info: @textInfo</h4>
}
<!-- 按鈕樣式參考 Counter 組件 -->
<button class="btn btn-primary" @onclick="UpdateText">Update Text</button>
<!-- 委托方式調用方法,可以傳入參數 -->
<button class="btn btn-primary" @onclick="(()=>{UpdateNumber(10);})">Update Number</button>
@code {
private string? textInfo = null;
private void UpdateText()
{
textInfo = "This is the new information";
}
private void UpdateNumber(int i = 0)
{
textInfo = $"This is number {i}";
}
}
在伺服器端項目中的 Home 頁面中引用 Demo 組件:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<Demo />
在 _Imports.razor 中引用 Demo 組件的命名空間:
@using MyComponents.Client.Pages
路由導航
在客戶端項目中添加一個 Start 組件,razor 代碼如下:
@*組件可以同時有多個路由*@
@page "/page"
@page "/pages/start"
@*可以使用組件名作路由*@
@attribute [Route(nameof(Start))]
@*頁面跳轉必須指定交互性,並註入導航管理器*@
@rendermode InteractiveAuto
@inject NavigationManager Navigation
<h3>Start</h3>
@*通過NavigationManager.NavigateTo方法跳轉到Counter組件*@
<button class="btn btn-primary" onclick="@(()=>Navigation.NavigateTo(nameof(Counter)))">Go to Counter</button>
@*執行完整的頁面重新載入*@
<button class="btn btn-primary" onclick="@(()=>Navigation.Refresh(true))">Refresh</button>
@code {
}
上面的代碼演示瞭如何使用路由和導航管理器進行頁面跳轉,組件可以同時有多個路由,也可以使用組件名作路由。
跳轉到其它組件時會用到增強導航,參考 增強的導航和表單處理 。
參數
組件參數
在客戶端項目中添加一個 BetterCounter 組件,razor 代碼如下:
@rendermode InteractiveAuto
<h3>BetterCounter</h3>
<p role="status">Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
//將目標成員表示為組件參數
[Parameter]
public int CurrentCount { get; set; }
private void IncrementCount()
{
CurrentCount++;
}
}
組件參數將數據傳遞給組件,使用組件類中包含 [Parameter] 特性的公共 C# 屬性進行定義,參考組件參數 和 單向綁定 。
使用組件時需要將目標成員作為組件參數傳遞給組件,如在 Home 頁面中引用 BetterCounter 組件:
<BetterCounter CurrentCount="100" />
<!-- 或 -->
<BetterCounter CurrentCount=@currentCount />
路由參數
路由器使用路由參數以相同的名稱填充相應的組件參數,路由參數名不區分大小寫,參考文檔 路由參數 。
在 BetterCounter 組件的 razor 代碼中添加如下路由:
@*路由參數(無約束)*@
@page "/BetterCounter/{CurrentCount}"
@*路由參數約束*@
@page "/BetterCounter/{CurrentCount:int}"
@*可選路由參數(與上面任意一個路由搭配使用實現可選效果)*@
@page "/BetterCounter"
為了實現可選路由參數,需要在組件中添加一個預設值:
//將目標成員表示為組件參數
//需要將參數屬性的類型更改為可為 null,這樣就可以分辨出它是否被指定了值
[Parameter]
public int? CurrentCount { get; set; }
//為可選參數指定預設值
protected override void OnInitialized()
{
base.OnInitialized();
CurrentCount = CurrentCount ?? 1;
}
添加一個 Titlet 成員來接受查詢字元串的參數:
//指定組件參數來自查詢字元串
//路由:/BetterCounter?Titlet=asd
[SupplyParameterFromQuery]
public string? Titlet { get; set; } = "BetterCounter";
-
路由參數:在添加組件的 @page 聲明時,通過將路由參數的名稱括在一對 { 大括弧 } 中,在URL中定義了路由參數,參考示例 路由參數 。
-
路由參數約束:以冒號為尾碼,然後是約束類型,約束類型參考文檔 路由約束。以路由 /BetterCounter/abs 為例:
- 沒有路由約束時會報類型轉換異常(字元串無法轉為int類型)
- 有路由約束時會顯示404錯誤(沒有匹配到該 URL)
-
可選路由參數:Blazor不明確支持可選路由參數,但通過在組件上添加多個 @page 聲明,可以輕鬆實現等效的路由參數,參考文章 可選路由參數。
-
查詢字元串:使用 [SupplyParameterFromQuery] 屬性指定組件參數來自查詢字元串,更多應用場景參考文檔 查詢字元串。
生命周期事件
以下簡化圖展示了 Razor 組件生命周期事件處理,參考文檔 生命周期事件:
在 Counter 組件中添加日誌記錄,觀察組件的生命周期:
@inject ILogger<Counter> log
//...
@code {
//...
protected override void OnInitialized()
{
log.LogInformation($"Initialized at {DateTime.Now}");
}
protected override void OnParametersSet()
{
log.LogInformation($"ParametersSet at {DateTime.Now}");
}
protected override void OnAfterRender(bool firstRender)
{
log.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
}
-
組件初始化 (OnInitialized{Async}) :專門用於在組件實例的整個生命周期內初始化組件,參數值和參數值更改不應影響在這些方法中執行的初始化。
-
設置參數之後 (OnParametersSet{Async}):在 OnInitialized 或 OnInitializedAsync 中初始化組件後或父組件重新呈現並變更參數時調用。
-
組件呈現之後 (OnAfterRender{Async}):OnAfterRender 和 OnAfterRenderAsync 組件以交互方式呈現,併在 UI 已完成更新(例如,元素添加到瀏覽器 DOM 之後)後調用。OnAfterRender 和 OnAfterRenderAsync 的 firstRender 參數:
- 在第一次呈現組件實例時設置為 true。
- 可用於確保初始化操作僅執行一次。
運行後導航到Counter界面,控制台輸出如下:
狀態更改
StateHasChanged 通知組件其狀態已更改。 如果適用,調用 StateHasChanged 會導致組件重新呈現。
將自動為 EventCallback 方法調用 StateHasChanged,也可以根據實際需求在組件中手動調用 StateHasChanged:
private async void IncrementCount()
{
currentCount++;
await Task.Delay(1000);
StateHasChanged();
currentCount++;
await Task.Delay(1000);
StateHasChanged();
currentCount++;
await Task.Delay(1000);
StateHasChanged();
}
上面的代碼,如果不調用 StateHasChanged 則點擊後只會顯示 1 ,用 StateHasChanged 後則會依次顯示 1 2 3 。
組件事件
嵌套組件的常見方案是在發生子組件事件時在父組件中執行某個方法(如子組件中的 onclick 事件),跨組件公開事件請使用 EventCallback ,父組件可向子組件的 EventCallback 分配回調方法。
在 Counter 組件中添加一個事件:
@code {
private int currentCount = 0;
//定義一個事件回調參數
[Parameter]
public EventCallback<int> OnCounterChange { get; set; }
private async Task IncrementCount()
{
currentCount++;
//觸發事件回調
await OnCounterChange.InvokeAsync(currentCount);
}
}
在另一個客戶端組件為 Counter 組件的事件分配回調方法(服務端組件會報錯):
<Counter OnCounterChange="UpdateCounter" />
@code {
private int currentCount = 0;
private void UpdateCounter(int val)
{
currentCount = val;
}
}