Blazor —— 用更能装X的方式编写组件(RenderTreeBuilder)

前言

razor 的方式编写组件,这个学习过前端的应该都可以完成,但很多时候,我们需要学习更加高深的技术,不光是可以来装X,更是可以把X升级成高处不胜寒的王者。所以就来教教如何使用纯代码的方式来编写组件。

在装X之前,先了解《探索razor组件背后的秘密,你绝对想象不到》。

装X大法——RenderTreeBuilder

用最常用的例子来说明,编写一个按钮组件。

razor 页面来编写组件:

<button class="btn btn-primary" @onclick="OnClick">
	@ChildContent
</button>

@code {
	[Parameter]public RenderFragment ChildContent { get; set; }
	[Parameter]public EventCallback<MouseEventArgs> OnClick { get; set; }
}

现在开始装X大法:

之前说过组件是一个类。

1. 创建组件类

先创建一个类,并继承 ComponentBase 基类,然后声明一些组件需要的参数:

public class MyButton : ComponentBase
{
	[Parameter]public RenderFragment ChildContent { get; set; }
	[Parameter]public EventCallback<MouseEventArgs> OnClick { get; set; }
}

2. 重写 BuildRenderTree 方法

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	//... 这里要写组件
}

重点就在如何使用 RenderTreeBuilder 对象。写法其实就是按照平时写 html 元素的顺序进行。

OpenElement

如果我需要写一个元素 button ,我的写法是 <button>...</button> 写一个开始标记和一对结束标记,然后再反过来填充元素里面的内容。

在代码中,使用 builder.OpenElement 表示开始元素,使用 builder.CloseElement 表示结束元素

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	builder.OpenElement(0, "button");
	//...
	builder.CloseElement();
}

系统会检测 CloseElement 所匹配的 OpenElement 所生成的元素。

AddAttribute

使用 builder.AddAttribute 给元素添加属性,也就是 <button 属性="值">...</button> ,当然 class 也是属性。

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	builder.OpenElement(0, "button");
	builder.AddAttribute(1, "class", "btn btn-primary");
	builder.CloseElement();
}

使用 builder.AddContent 添加元素内的内容,即 <button>内容</button>

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	builder.OpenElement(0, "button");
	builder.AddAttribute(1, "class", "btn btn-primary");
	builder.AddContent(2, ChildContent);
	builder.CloseElement();
}

调用顺序必须是按照真实的 html 写法,以下方式会报错:

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	builder.OpenElement(0, "button");
	builder.AddContent(1, ChildContent);
	builder.AddAttribute(2, "class", "btn btn-primary");
	builder.CloseElement();
}

InvalidOperationException: Attributes may only be added immediately after frames of type Element or Component

系统会认为,如果你已经决定添加了元素之间的内容,就表示开始元素已经结束(即形成了 >)。

EventCallback

使用 EventCallback 类来创建触发某个 html 事件并绑定到你设置的参数 OnClick 中。

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
	builder.OpenElement(0, "button");
	builder.AddAttribute(1, "class", "btn btn-primary");
	builder.AddAttribute(2, "onclick", EventCallback.Factory.Create(this, OnClick));
	builder.AddContent(3, ChildContent);
	builder.CloseElement();
}

3. 最后的结果

@inject IJSRuntime JS
<MyButton OnClick="@(e=>JS.InvokeVoidAsync("alert","点击了按钮"))">按钮</MyButton>

在这里插入图片描述
没有页面,只有代码的方式,就问你看到这样的源码屌不屌呢?

关于序列号

你会看到每次调用一个方法,都会要求有一个 sequence 参数,称为序列号。该参数与代码行号相关,而不与执行顺序相关

Razor 组件文件 (.razor) 始终被编译。 与解释代码相比,编译具有潜在优势,因为编译步骤可用于注入信息,从而在运行时提高应用性能。
这些改进的关键示例涉及序列号。 序列号向运行时指示哪些输出来自哪些不同的已排序代码行。 运行时使用此信息在线性时间内生成高效的树上差分,这比常规树上差分算法通常可以做到的速度快得多。

以下是关于系列号的建议和指南:

  • 如果动态生成序列号,则应用性能会受到影响。
  • 该框架无法在运行时自动创建自己的序列号,因为除非在编译时捕获了必需的信息,否则这些信息不存在。
  • 不要编写手动实现的冗长 RenderTreeBuilder 逻辑块。 优先使用 .razor 文件并允许编译器处理序列号。 如果无法避免 RenderTreeBuilder·手动逻辑,请将较长的代码块拆分为封装在 OpenRegion/CloseRegion 调用中的较小部分。 每个区域都有自己的独立序列号空间,因此可在每个区域内从零(或任何其他任意数)重新开始。
  • 如果序列号已硬编码,则差分算法仅要求序列号的值增加。 初始值和间隔不相关。 一个合理选择是使用代码行号作为序列号,或者从零开始并以 1 或 100 的间隔(或任何首选间隔)增加。
  • Blazor 使用序列号,而其他树上差分 UI 框架不使用它们。 使用序列号时,差分速度要快得多,并且 Blazor 的优势在于编译步骤可为编写 .razor 文件的开发人员自动处理序列号。

关于序列号的更多内容可参考微软官方文档

结论

用代码的方式创建组件固然很牛,但是也比较影响开发效率,而且也提升了一个人对于 html 知识的高度,你对组件、页面布局要有很强的具象思维,特别对于复杂组件。但对于个人来说,确实是实力的展现,同样也需要付出更多。装X有风险,且装且珍惜!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我 Teacher 周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值