本文主要討論Blazor事件內容,由於blazor事件部分很多,所以會分成上下兩篇,本文為第二篇。
雙向繫結
概述
如圖所示
- 當點選單項繫結的時候,MyOnewayComponent裡的屬性值會發生變化,這種變化是單項的,僅僅只是本地副本的值的變化,並不會引發父頁面的值發生變化。但當點選父頁面的Click Me的時候,會修改MyOnewayComponent的屬性值會被修改。所以單項繫結強調的是佔位,以達到動態輸出的目的。
- 當點選雙向繫結的時候,三個值會同步發生變化。即便點選父頁面的Click Me,也不會覆蓋掉MyTwoWayComponent的屬性值,這說明父頁面和MyTwoWayComponent頁面的值發生了雙向繫結,會導致資料同步變化。
- 雙向繫結,繫結的是Blazor元件和dom元素,就像是巨集指令一樣。也就是說,當該元件首次執行時,輸入框的值來自於CurrentValue屬性,當使用者輸入新的值後,CurrentValue也將會被設定成新的值。
示例
雙向繫結有一個重要特徵就是使用@bind-進行資料繫結,之前我建立了兩個元件,我們來看一下這兩個元件的原始碼:MyOnewayComponent:
1: <div>
2: MyComponent CounterValue is @CounterValue
3: </div>
4: <button @onclick=UpdateCounterValue>Update</button>
5: @code {
6:
7: [Parameter]
8: public int CounterValue { get; set; }
9:
10: void UpdateCounterValue()
11: {
12: CounterValue++;
13: }
14: }
MyTwoWayComponent:
1: <div>
2: MyComponent CounterValue is @CounterValue
3: </div>
4: <button @onclick=UpdateCounterValue>Update</button>
5: @code {
6:
7: [Parameter]
8: public int CounterValue { get; set; }
9:
10: [Parameter]
11: public EventCallback<int> CounterValueChanged { get; set; }
12:
13: async Task UpdateCounterValue()
14: {
15: CounterValue++;
16: await CounterValueChanged.InvokeAsync(CounterValue);
17: }
18: }
以上程式碼可以看到有明顯的不同,MyTwoWayComponent包含一個EventCallback型別的屬性,其命名是CounterValueChanged,看起來像是屬性值字尾Changed,其呼叫方法也變成了async Task,該方法表明,當CounterValue發生變化的時候,會通過CounterValueChanged來通知事件源頁面該值發生了變化。額外嘗試一下,如果我們直接使用MyOnewayComponent 來演示雙向繫結,會發生什麼,我們使用如下程式碼執行一下看看:
1: <MyOnewayComponent @bind-CounterValue="@currentCount" />
執行後,發現報錯了,錯誤資訊是:Unhandled exception rendering component: Object of type 'BlazorApp.Client.Pages.MyOnewayComponent' does not have a property matching the name 'CounterValueChanged'。由此可見,我們的命名規則是強制的,其必須是所繫結EventCallBack的屬性名字尾Changed。
BuildTree原始碼
1: #pragma warning disable 1998
2: protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
3: {
4: __builder.AddContent(8, "Click me");
5: __builder.CloseElement();
6: __builder.AddMarkupContent(9, "\r\n<br>\r\n<br>\r\n\r\n");
7: __builder.OpenComponent<BlazorApp.Client.Pages.MyOnewayComponent>(10);
8: __builder.AddAttribute(11, "CounterValue", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<System.Int32>(
9: #nullable restore
10: currentCount
11:
12: #line default
13: #line hidden
14: #nullable disable
15: ));
16: __builder.AddAttribute(12, "CounterValueChanged", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => currentCount = __value, currentCount));
17: __builder.CloseComponent();
18: }
- 8-15行是單項繫結的內容
- 16-18行是雙向繫結的內容
級聯值和引數
概述
級聯值和引數是一種將值從元件傳遞到其所有子元件的方法,在Blazor中,採用CascadingValue來實現,子元件通過宣告同一型別的屬性(用[CascadingParameter]屬性修飾)來收集並賦值。當級聯值發生更新的時候,這種更新將傳遞到所有的子元件,同時這元件將會自動呼叫StateHasChanged 。一般情況下,我們的CascadingValue中可能會需要傳遞多個值的變化,那麼這種變化是如何進行的呢。是通過兩種方式,一種是型別推導,一種是命名傳值。型別推導
我建立了兩個元件,分別是FirstComponent,SecondComponent。FirstComponent原始碼如下:
1: <CascadingValue Value="@CascadingIndex">
2: <CascadingValue Value="@CascadingName">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6: @code {
7: int CascadingIndex = 10000;
8:
9: string CascadingName = "FirstComponent";
10: }
SecondComponent原始碼如下:
1:
2: <h3>SecondComponent</h3>
3: <p>
4: CascadingValue Is <strong>@SecondValue</strong>
5: </p>
6: @code {
7: [CascadingParameter] int SecondValue { get; set; }
8: }
傳值的過程中,我們只有一個int型別的屬性,所以該值會顯示10000,如下圖所示:
如果我們修改一下FirstComponent的原始碼,將其中的string型別的屬性刪除掉,同時增加一個新的int型別的屬性,如下原始碼所示:
1: <CascadingValue Value="@CascadingIndex">
2: <CascadingValue Value="@Total">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6:
7: @code {
8: int CascadingIndex { get; set; } = 10000;
9:
10: int Total{ get; set; } = 2;
11: }
執行結果如下:
由此可見,當子元件遇到多個相同型別的屬性的時候,會選擇離子元件最近的屬性的值並傳遞到自己的屬性中去。
命名傳值
命名賦值就很單純了,主要考慮繫結正確的名稱就行。修改後FirstComponent的原始碼如下,需要指定Name:
1: <CascadingValue Value="@CascadingIndex" Name="CascadingIndex">
2: <CascadingValue Value="@Total" Name="Total">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6:
7: @code {
8: int CascadingIndex{ get; set; } = 10000;
9:
10: int Total{ get; set; } = 2;
11: }
SecondComponent原始碼如下,可以指定名稱已接收值的傳遞
1: <h3>SecondComponent</h3>
2: <p>
3: CascadingValue Is <strong>@SecondValue</strong>
4: </p>
5: @code {
6: [CascadingParameter(Name = "CascadingIndex")] int SecondValue { get; set; }
7: }
執行後的結果如下,值又變回了一萬
有朋友可能會想,我不想設定SecondComponent中CascadingParameter的Name值,但是我可以設定成FirstComponent中某個已經繫結的Name的名稱。如下所示:
FirstComponent原始碼不變,SecondComponent原始碼如下:
1: <h3>SecondComponent</h3>
2: <p>
3: CascadingValue Is <strong>@Total</strong>
4: </p>
5: @code {
6: [CascadingParameter] int Total { get; set; }
7: }
執行結果如下所示:
由此可見,不設定子元件中CascadingParameter的Name值,是無法接收傳遞的值的。
效能問題
預設情況下,Blazor會持續監控級聯值的變化,並將其傳遞到所有子元件中,這將會佔用一定的資源,並可能導致效能問題。
如果我們可以確定,我們的級聯值不會發生變化,可以設定CascadingValue中引數IsFixed的值為true,這樣的Blazor就不會監控級聯值的變化了。
1: <CascadingValue Value="@CascadingIndex" IsFixed="true">
2: <SecondComponent></SecondComponent>
3: </CascadingValue>