Blazor帶我重玩前端(六)

艾心❤發表於2020-09-02

本文主要討論Blazor事件內容,由於blazor事件部分很多,所以會分成上下兩篇,本文為第二篇。

雙向繫結

概述

8

如圖所示

  • 當點選單項繫結的時候,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,如下圖所示:

      1

      如果我們修改一下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:  }

      執行結果如下:

      2

      由此可見,當子元件遇到多個相同型別的屬性的時候,會選擇離子元件最近的屬性的值並傳遞到自己的屬性中去。

      命名傳值

      命名賦值就很單純了,主要考慮繫結正確的名稱就行。修改後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:  }

      執行後的結果如下,值又變回了一萬

      3

      有朋友可能會想,我不想設定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:  }

      執行結果如下所示:

      4

      由此可見,不設定子元件中CascadingParameter的Name值,是無法接收傳遞的值的。

      效能問題

      預設情況下,Blazor會持續監控級聯值的變化,並將其傳遞到所有子元件中,這將會佔用一定的資源,並可能導致效能問題。

      如果我們可以確定,我們的級聯值不會發生變化,可以設定CascadingValue中引數IsFixed的值為true,這樣的Blazor就不會監控級聯值的變化了。

         1:  <CascadingValue Value="@CascadingIndex" IsFixed="true">
         2:      <SecondComponent></SecondComponent>
         3:  </CascadingValue>

      相關文章