上篇提到 Blazor 元件的高階寫法,是採用擴充套件方法對 HTML 元素和元件進行擴充套件,以便於書寫元件結構和程式碼閱讀。本篇主要介紹擴充套件方法實現的思路。
- 什麼是擴充套件方法
- 要擴充套件哪個類
- 擴充套件方法的實現
1. 什麼是擴充套件方法
若要對一個 C# 型別新增新方法,一是修改原始碼,二是派生類,三是擴充套件方法。前兩者不是萬能的,第一種我們不一定有原始碼,第二種型別不一定能繼承,只有第三種是萬能的方法,在專案中新建一個擴充套件型別即可對任何型別進行擴充套件。
一個擴充套件方法需要有如下條件:
- 新增擴充套件類,類必須宣告 static 修飾符
- 新增方法,方法必須宣告 static 修飾符
- 方法第一個引數必須是擴充套件型別,且要有 this 關鍵字
//擴充套件型別
static class Extension
{
static void SayHello (this string name)
{
Console.WriteLine($"Hello, I'm {name}.");
}
}
//測試
var name = "Known";
name.SayHello();
//輸出
Hello, I'm Known.
2. 要擴充套件哪個類
上篇提到元件的高階寫法,需要覆寫元件的 BuildRenderTree 方法,這個方法有唯一型別引數 RenderTreeBuilder,對,就是這個型別,我們從它開始擴充套件一切 HTML 基本元素及自定義元件。
下面看看 RenderTreeBuilder 有哪些原生方法。
- OpenElement,開啟一個元素,呈現
<div>
等開始標籤 - CloseElement,關閉一個元素,呈現
</div>
等關閉標籤 - OpenComponent,開啟一個元件,呈現
<MyComponent>
- CloseComponent,關閉一個元件,呈現
</MyComponent>
- AddAttribute,新增元素和元件的屬性,有8個過載方法
- AddMultipleAttributes,一次性新增多個屬性,引數為字典型別
- AddContent,新增標籤內部的內容,有6個過載方法
- AddMarkupContent,新增原始 HTML 字元內容
- AddElementReferenceCapture,新增元素物件參考,透過它可獲取元素物件的例項
- AddComponentReferenceCapture,新增元件物件參考,透過它可獲取元件物件的例項
掌握以上這些方法的使用後,我們就可以開發擴充套件我們需要的元素和元件。
3. 擴充套件方法的實現
由於 HTML 元素標籤及其屬性眾多,為了方便且全面適配所有屬性,增加一個屬性建造者型別 AttributeBuilder 來管理元素屬性。建造者型別如下:
public class AttributeBuilder
{
private readonly RenderTreeBuilder builder;
public AttributeBuilder(RenderTreeBuilder builder)
{
this.builder = builder;
}
public AttributeBuilder Add(string name, object value)
{
if (value != null)
builder.AddAttribute(1, name, value);
return this;
}
public AttributeBuilder Id(string id) => Add("id", id);
public AttributeBuilder Name(string name) => Add("name", name);
...
}
新增一個 HTML 元素擴充套件類,用於擴充套件 HTML 元素,程式碼示例如下:
public static class HtmlExtension
{
//通用元素擴充套件方法
public static void Element(this RenderTreeBuilder builder, string name, Action<AttributeBuilder> child = null)
{
builder.OpenElement(0, name);
var attr = new AttributeBuilder(builder);
child?.Invoke(attr);
builder.CloseElement();
}
//div標籤
public static void Div(this RenderTreeBuilder builder, Action<AttributeBuilder> child) => builder.Element("div", child);
...
}
下面寫一個元素擴充套件方法的示例,並將呈現的 HTML 結構與 C# 程式碼進行比對,直觀感受一下高階寫法的妙處。
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.Div(attr => //<div id="myPanel" class="panel">
{ //
attr.Id("myPanel").Class("panel"); //
builder.Div(attr => // <div class="header">
{ //
attr.Class("header"); //
//這裡構造 Panel 頭部內容 //
}); // </div>
builder.Div(attr => // <div class="body">
{ //
attr.Class("body"); //
//這裡構造 Panel 身體內容 //
}); // </div>
}); //</div>
}
再次最佳化一下擴充套件方法的示例,下面程式碼看起來是不是更整齊了一些。
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.Div("myPanel", "panel", attr => //<div id="myPanel" class="panel">
{ //
builder.Div("header", attr => // <div class="header">
{ //
//這裡構造 Panel 頭部內容 //
}); // </div>
builder.Div("body", attr => // <div class="body">
{ //
//這裡構造 Panel 身體內容 //
}); // </div>
}); //</div>
}