[AspNetCore 3.0 ] Blazor 服务端组件 Render, RenderFragment ,RenderTreeBuilder, CascadingValue/CascadingParameter 等等
一、组件
支撑Blazor的是微软的两大成熟技术,Razor模板和SignalR,两者的交汇点就是组件。通常,我们从ComponentBase派生的类型,或者创建的.razor 文件,就可以称作组件。基于这两大技术,组件也就具备了两大功能,1、生成html片段;2、维护组件状态。这里我们来说一下组件最基本的功能,生成html片段。
二、RenderTreeBuilder,RenderFragment
我们知道,浏览器处理HTML 文档时会将所有的标签都挂到一颗文档树中,无论一段HTML来自哪里,总会被这棵树安排的明明白白。换句话说,如果有根线的话,我们可以依靠这棵树把所有的标签都串起来,而在Blazor组件中也有这么一根线,这根线就是RenderTreeBuilder,拿这根线的人就是Blazor框架。
备注一下:以下涉及的代码如果没有特别说明,都是指写在.cs文件中,继承 Microsoft.AspNetCore.Components.ComponentBase 的组件类。
下面用代码看看这根线。 新建一个Blazor 应用 项目,新增 一个c#类,MyComp 继承 Microsoft.AspNetCore.Components.ComponentBase,然后override 一下,找到如下方法:
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);//加断点
}
加个断点,在项目的 Pages\Index.razor 里加上一行。
如果不想代码执行两次,就在Pages_Host.cshtml 里修改一下rendermode
@(await Html.RenderComponentAsync(RenderMode.Server))
F5跑起来,虽然没有任何输出,但是断点命中了,RenderTreeBuilder这根线确实串起了我们的组件。
现在让我们看看,RenderTreeBuilder 可以做什么。
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, " BuildRenderTree 使用 AddMarkupContent 输出 Html 。");
// base.BuildRenderTree(builder);
}
再次跑起来,我们发现页面上多了我们加的span.也就是说HTML的输出,靠的是调用RenderTreeBuilder上的各种方法加上的。组件的基本原理也就是这样,一个RenderTreeBuilder 进入不同组件的 BuildRenderTree 方法,方法内 通过RenderTreeBuilder上的add.. open.. 方法把我们想要输出的部分,挂载到builder上,最终输出到浏览器。
接下来,我们考察一下BuildRenderTree方法, 用委托描述一下,我们发现这就是一个Action.
在标题里我们提到了RenderFragment, 查看一下它的定义。
public delegate void RenderFragment(RenderTreeBuilder builder);//还是一个 Action,或者说,BuildRenderTree 就是一个RenderFragment
我们发现和前面的BuildRenderTree 在签名上一模一样,既然blazor会使用RenderTreeBuilder 去调用BuildRenderTree 方法,那么RenderFragment会不会也被调用?
让我们暂时离开组件MyComp,转到Index.razor 内加一段code
@code{
RenderFragment MyRender=(builder) => builder.AddMarkupContent(0, "当前输出来自:Index.razor 组件, MyRender 字段。 ");
}
在之前我们声明 MyComp组件之后,再加一行调用 @MyRender.
完整的Index.razor
@page "/"
@MyRender
@code{
RenderFragment MyRender = (builder) => builder.AddMarkupContent(0, " 用法一般如下
//模板中(Index.razor)
RenderFragment
当前输出来自:Index.razor 组件, MyRender 字段。
");
}
两段信息,如愿输出,证明blazor能够识别出模板中的 RenderFragment ,并自动调用。
既然我们在组件模板中(Index.razor)书写RenderFragment ,当然有其他方式可以不用拼凑字符串。
RenderFragment AnotherRender =@模板写法的RenderFragment
;
加上调用 @AnotherRender,跑起来,三段信息。
至此,我们对RenderFragment 有了一个大概的了解,它是一个函数,内部打包了我们的输出内容。在模板中我们可以使用,@xxxrender将其就地展开输出,在c#环境下我们可以通过 xxxrender(builder)的形式进行调用(比如在BuildRenderTree方法内调用)。又因为其本身就是一个委托函数,因此我们即可以在组件内使用,也可以自由的在组件之间传递, 完成对输出内容及逻辑的复用。
同时,为了更好的配合RenderFragment 使用,Blazor中还提供了一个工厂委托,RenderFragment , 即 Func