1. 程式人生 > >通過Blazor使用C#開發SPA單頁面應用程式(3)

通過Blazor使用C#開發SPA單頁面應用程式(3)

  今天我們來看看Blazor開發的一些基本知識。

一、Blazor元件結構


 Blazor中元件的基本結構可以分為3個部分,如下所示:

//Counter.razor
//Directives section 指令部分 @page "/counter" //Razor HTML section Razor HTML部分 <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" onclick="@IncrementCount">Click me</button> //code sections 功能部分 @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }

  指令部分:

  • 路由   - @page
  • DI     - @inject
  • 匯入庫 - @using

 

  Razor HTML 部分:

        Razor HTML語法是C#程式碼與HTML的結合。此部分最終在瀏覽器中呈現。

  指令部分:

       元件中的函式部分包含使用者操作函式(事件方法),區域性變數和從/向父/子元件傳遞的屬性。

  當然如果願意,這部分也可以單獨寫道類檔案中。

 

二、Blazor的屬性和引數


   我們看看屬性和引數,

<button id="btnClickMe" class="btn btn-primary" 
onclick="@IncrementCount">Click me</button>

  在這裡,在按鈕元素的id,class和onclick被稱為HTML屬性。

  類似地,元件的定義方式與HTML元素相同,

 //MyDemo.razor
@page "/myDome"

<h3>MyDemo</h3>

<ChildComponent Title="來自MyDemo"></ChildComponent>

  在Child Component中,該屬性Title與裝飾的子元件函式部分中的屬性匹配 [Parameter] 關鍵字。

//ChildComponent.razor

//Child Component <div> <p>標題 : @Title</p> </div> @code { [Parameter] private string Title { get; set; } }

 執行效果如下:

 

三、Blazor的資料繫結


 

  Blazor的資料繫結同時提供了單向繫結和雙向繫結兩種機制。

  (一)單向繫結:

  單向繫結在Blazor中簡單直接,無需任何UI重新整理。還記得Counter示例嗎,他顯示了單向資料繫結,

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="@IncrementCount">Click me</button>

@code {
    int currentCount = 0;

    void IncrementCount()
    {
        currentCount++;
    }
}

  此處 @currentCount 值根據點選按鈕的數量遞增Click me。<p>標記元素中的值會自動重新整理,無需任何元件重新整理。

   (二)雙向繫結:

 在Blazor中可以實現雙向繫結,與一些流行的JS語言框架相比,Blazor為雙向繫結提供了多種實現方式,Blazor可以優雅地進行編寫。

 

 (1) @bind屬性在Blazor中提供雙向資料繫結。下面的示例複選框演示在同一組件中的bind屬性,

@page "/myDome"


<span>請選擇:</span>
<input type="checkbox" @bind="myChecked" />
<p>我選擇了 : @myChecked.ToString()</p>


@code {
    bool myChecked { get; set; } = true;
}

   執行效果如下,是不是很簡單,很優雅。

   再來一個控制樣式表的例子看看,

@page "/myDome"

<p>
    <span>顯示/隱藏:</span>
    <input type="checkbox" @bind="myChecked" />
</p>
<p style="display:@(myChecked ? "inline":"none")">看到我了</p>


@code {
    bool myChecked { get; set; } = true;
}

 (2) @bind屬性在Blazor中提供雙向資料繫結,但是隻提供了預設的繫結事件,如果們想在不同的時機觸發雙向繫結該怎麼辦呢,別急同樣很簡單的,我們看看下面的程式碼,展示了幾種繫結例項,

@page "/myDome"

<p>
    <span>onchange 方式一</span>
    <input @bind="changeString" />
</p>
<p>
    <span>onchange 方式二</span>
    <input type="text"
           value="@changeString"
           @onchange="@((UIChangeEventArgs _e) => changeString = _e.Value.ToString())" />
</p>
<p>
    <span>onchange 方式三</span>
    <input @bind-value="changeString" @bind-value:event="onchange" />
</p>
<p>
    <span>oninput</span>
    <input @bind-value="changeString" @bind-value:event="oninput" />
</p>
<p>這是我輸入的內容: @changeString</p>

@code {
    string changeString = "";
}

  執行效果如下,

 

    呈現元件時, input元素value的值來自changeString。 當用戶在文字框中鍵入內容並離開時, 將觸發事件onchange更改changeString的值。原則上, @bind將表示式的當前值value與changeString相關聯, 並使用註冊的處理程式來處理更改。
    除了使用@bind語法處理onchange事件之外, 還可以通過使用event引數 (@bind-value:event) 指定@bind-value屬性, 使用其他事件來繫結屬性或欄位。例如第四個文字框就是繫結changeString採用oninput事件的屬性,以到達在文字框的值更改時激發。

   (三)元件之間繫結:  

 (1)繫結可識別元件引數, @bind-{property}可在其中跨元件繫結屬性值。

 

//MyDemo.razor

@page "/myDome"

<h1>Parent Component</h1>
<p>當前時間: @ParentNow</p>
<hr />

<ChildComponent @bind-Now="ParentNow" />

<hr />
<button class="btn btn-primary" @onclick="@ChangeTheYear">
    更新當前時間
</button>

@code {
    [Parameter]
    public DateTime ParentNow { get; set; } = DateTime.Now;

    private void ChangeTheYear()
    {
        ParentNow = DateTime.Now;
    }
}

 

//ChildComponent.razor

<h2>Child Component</h2>

<p>當前時間: @Now</p>

@code {
    [Parameter]
    public DateTime Now { get; set; }

    [Parameter]
    public EventCallback<DateTime> NowChanged { get; set; }
}

   以上程式碼中,子元件 (ChildComponent) 具有一個Now元件引數和NowChanged回撥引數,父元件MyDemo使用ChildComponent並將ParentNow引數從父級繫結到子元件上Now的引數上,如果通過點選MyDemo中的"更新當前時間"按鈕來更改屬性的值, Now則將更新ChildComponent屬性,將新值呈現在 UI中。其中,引數Now是可繫結的, 因為它具有NowChanged與引數型別匹配的伴隨事件。按照約定,其等效於

<ChildComponent @bind-Now="ParentNow" @bind-Now:event="NowChanged" />

  執行效果:

 

 (2)元件之間傳遞的資料通過元件屬性及其屬性對映發生,此方法使用委託Action<T>型別。

//MyDemo.razor

@page "/myDome"

<h3>Parent Component</h3>
<p>來自Child元件: @childString</p>
<p>
    <input @bind="inputText" />
</p>
<hr />
<ChildComponent ToChild="@inputText"
                FromChild="@ReceivedFromChild">
</ChildComponent>

@code{
    private string inputText = "";
    private string childString = "";


    private void ReceivedFromChild(string str)
    {
        childString = str;
        StateHasChanged();
    }
}
//ChildComponent.razor

<h4>Child Component</h4>
<p>
    <input @bind="inputText" />
    <button @onclick="@PassToParent">顯示到Parent元件</button>
</p>
<p>來自Parent元件 : @ToChild</p>

@code{
    [Parameter]
    private string ToChild { get; set; }

    [Parameter]
    Action<string> FromChild { get; set; }

    private string inputText = "";

    private void PassToParent()
    {
        FromChild(inputText);
    }
}

   這裡FromChild是ChildComponent中的屬性,屬性使用Action<string>資料型別將值從Child傳遞給Parent Component。在Parent中,有相應的接收器函式ReceivedFromChild和字串引數,這將在ChildComponent中按鈕單擊並觸發通知時觸發PassToParent,但是為了通知狀態已在父元件中更改,我們使用StateHasChanged()的內建Blazor函式通知元件其狀態已更改。

  執行效果如下:

 

四、 生命週期方法


 (1) OnInitializedAsync和OnInitialized方法,執行程式碼來初始化元件。要執行非同步操作,請在操作上使用OnInitializedAsync和await關鍵字。

(2)OnParametersSetAsync和OnParametersSet當元件已接收到的引數從其父和值被分配給屬性被呼叫。這些方法在元件初始化後以及每次呈現元件時執行。

(3)OnAfterRenderAsync並OnAfterRender在元件完成渲染後呼叫。此時填充元素和元件引用。使用此階段使用呈現的內容執行其他初始化步驟,例如啟用對呈現的DOM元素進行操作的第三方JavaScript庫。

 

五、級聯值和引數


 

  在某些情況下, 使用元件引數將資料從祖先元件流式傳輸到附屬元件是不方便的, 尤其是在有多個元件層時。 級聯值和引數通過提供一種方便的方法, 使上級元件為其所有子代元件提供值。 級聯值和引數還提供了一種方法來協調元件。

  Blazor提供了一種在整個RenderTree(所有元件)中傳遞資料的方法,使用CascadingValue和CascadingParameter不需要傳遞作為元件屬性,並且可以通過裝飾屬性[CascadingParameter]而不用在RenderTree(子元件)中接收值[Parameter]。

//MyDemo.razor

@page "/myDome"

<p><span>姓名:</span><input @bind="@pName" /></p>
<p><span>年齡:</span><input @bind="@pAge" /></p>
<hr />
<CascadingValue Value="@pName" Name="ProfileName">
    <CascadingValue Value="@pAge" Name="ProfileAge">
        <ParentComponent />
    </CascadingValue>
</CascadingValue>

@code {
    private string pName { get; set; } = "張三";
    private int pAge { get; set; } = 35;
}
//ParentComponent.razor

<div style="background-color:darkgray">
    <p>Parent Component</p>
    <div style="padding:10px;">
        <ChildComponent />
    </div>
</div>
//ChildComponent.razor

<div style="background-color:beige">
    <p>Child Component</p>
    <p>輸入的 姓名: @Name  , 年齡 : @Age.ToString()</p>
</div>

@code{
    [CascadingParameter(Name = "ProfileName")]
    string Name { get; set; }
    [CascadingParameter(Name = "ProfileAge")]
    int Age { get; set; }
}

  程式碼中MyDemo的姓名、年齡穿透ParentComponent直接級聯到ChildComponent中。

  在這裡CascadingParameter,Name引數必須與Name帶有CascadingValue元件的屬性匹配,如果我們沒有提到任何Name,則CascadingParameter中的變數型別與CascadingValue中的Value屬性匹配。

  執行效果:

 

六、路由


 

  我們在看一個SPA中一個基本但很重要的功能路由。客戶端路由可以通過使用@page指令裝飾元件來在Blazor中完成。

@page "/myDome"
@page "/myDome/{text}"

  @page在上面的示例中應用了兩個指令。

  第一個允許在沒有引數的情況下導航到元件。

  第二個@page指令採用{text}route引數並將值賦給Text屬性。

 

好了今天Blazor的元件開發就先學習到這,有意猶未盡的可以檢視官方文件深入學習。

&n