关于后端:Masa-Blazor自定义组件封装

2次阅读

共计 7428 个字符,预计需要花费 19 分钟才能阅读完成。

前言

理论我的项目中总能遇到一个 ” 组件 ” 不是根底组件然而又会频繁复用的状况, 在开发 MASA Auth 时也封装了几个组件。既有简略定义 CSS 款式和界面封装的组件(GroupBox),也有带肯定组件外部逻辑的组件 (ColorGroup)。
本文将一步步演示如何封装出一个如下图所示的 ColorGroup 组件, 将 MItemGroup 革新为 ColorGroup, 点击抉择预设的色彩值。

<!– more –>

MASA Blazor 介绍

组件展现

MASA Blazor 提供丰盛的组件(还在减少中), 篇幅限度上面展现一些我罕用到的组件

Material Design + BlazorComponent

BlazorComponent 是一个底层组件框架,只提供性能逻辑没有款式定义,MASA Blazor 就是 BlazorComponent 根底实现了 Material Design 款式规范。如下图所示,你能够基于 Ant Design 款式规范实现一套 Ant Design Blazor(尽管曾经有了,如果你想这么做齐全能够实现)。

我的项目创立

首先确保已装置 Masa Template(防止手动援用 MASA Blazor), 如没有装置执行如下命令:

dotnet new --install Masa.Template

创立一个简略的 Masa Blazor Server App 我的项目:

dotnet new masab -o MasaBlazorApp

组件封装

Blazor 组件封装很简略,不须要和 vue 一样进行注册,新建一个 XXX.razor 组件就是实现了 XXX 组件的封装,略微简单些的是须要自定义组件外部逻辑以及定义凋谢给用户(不同的应用场景)的接口(参数),即依据需要减少 XXX.razor.cs 和 XXX.razor.css 文件。

界面封装

在相熟各种组件性能的前提下找出须要的组件组装起来简略实现想要的成果。这里我应用 MItemGroup、MCard 及 MButton 实现 ColorGroup 的成果。MItemGroup 做色彩分组,且自身提供每一项激活的性能。MCard 作为色彩未抉择之前的遮罩层,实现含糊成果。MButton 作为色彩展现载体及激活 MItem。通过 MCard 的 style 设置透明度辨别选中、未选中两种状态。

也可通过减少一个对比色的圆形边框标记选中状态,相干 CSS 参考:https://www.dailytoolz.com/cs…

新建 ColorGroup.Razor 文件,代码如下:

<MItemGroup Mandatory Class="m-color-group d-flex mx-n1">
    <MItem>
        <MCard Class="elevation-0" Style="@($"transition: opacity .4s ease-in-out; {(context.Active ? "":"opacity: 0.5;")}")">
            <MButton Fab class="mx-1 rounded-circle" OnClick="context.Toggle"
                     Width=20 Height=20 MinWidth=20 MinHeight=20 Color="red">
            </MButton>
        </MCard>
    </MItem>

    <MItem>
        <MCard Class="elevation-0" Style="@($"transition: opacity .4s ease-in-out; {(context.Active ? "":"opacity: 0.5;")}")">
            <MButton Fab class="mx-1 rounded-circle" OnClick="context.Toggle"
                     Width=20 Height=20 MinWidth=20 MinHeight=20 Color="blue">
            </MButton>
        </MCard>
    </MItem>

    <MItem>
        <MCard Class="elevation-0" Style="@($"transition: opacity .4s ease-in-out; {(context.Active ? "":"opacity: 0.5;")}")">
            <MButton Fab class="mx-1 rounded-circle" OnClick="context.Toggle"
                     Width=20 Height=20 MinWidth=20 MinHeight=20 Color="green">
            </MButton>
        </MCard>
    </MItem>
</MItemGroup>

批改 Index.Blazor 文件 减少 ColorGroup 应用代码,Masa.Blazor.Custom.Shared.Presets 为自定义组件门路,即命名空间:

<Masa.Blazor.Custom.Shared.Presets.ColorGroup>
</Masa.Blazor.Custom.Shared.Presets.ColorGroup>

运行代码,看到多出三个不同色彩的圆型:

Masa Blazor 是 Vuetify 的 Blazor 实现,所有的 Class 除了 m -color-group 都是 Vuetify 提供的 class 款式。

自定义参数

通过第一局部能够看到封装的组件体面(界面)有了,然而这个体面是“死”的,不能依据不同的应用场景展现不同的成果,对于 ColorGroup 而言,最根本的需要就是应用时能够自定义显示的色彩值。
Blazor 中通过 [Parameter] 个性来申明参数,通过参数的形式将上叙代码中写死的值改为通过参数传入。如按钮的大小、色彩以及 MItemGroup 的 class 和 style 属性等。同时减少组件的里子(组件逻辑),点击不同色彩按钮更新 Value。

新建 ColorGroup.Razor.cs 文件,增加如下代码:

public partial class ColorGroup
{[Parameter]
    public List<string> Colors {get; set;} = new();

    [Parameter]
    public string Value {get; set;} = string.Empty;

    [Parameter]
    public EventCallback<string> ValueChanged {get; set;}

    [Parameter]
    public string? Class {get; set;}

    [Parameter]
    public string? Style {get; set;}

    [Parameter]
    public int Size {get; set;} = 24;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {if (firstRender)
        {if (Colors.Any())
            {await ValueChanged.InvokeAsync(Colors.First());
            }
        }
        await base.OnAfterRenderAsync(firstRender);
    }
}

下面的代码能够看到 Value 参数有个与之对应的 ValueChanged 参数,目标是为了能在组件内部接管 Value 值的变更,通过调用 ValueChanged.InvokeAsync 告诉组件内部 Value 值更新。

须要留神的是应尽量减少参数定义,太多的参数会减少组件出现的开销。
缩小参数传递,能够自定义参数类(本文示例为独自定义多个参数)。如:

@code {[Parameter]
    public TItem? Data {get; set;}

    [Parameter]
    public GridOptions? Options {get; set;}
}

同时更新 ColorGroup.Razor 文件中代码,循环 Colors 属性显示子元素以及减少 MButton 的点击事件,更新 Value 值:

<MItemGroup Mandatory Class="@($"m-color-group d-flex mx-n1 {@Class}")" style="@Style">
    @foreach (var color in Colors)
    {
        <MItem>
            <MCard Class="elevation-0" Style="@($"transition: opacity .4s ease-in-out; {(context.Active ? "":"opacity: 0.5;")}")">
                <MButton Fab class="mx-1 rounded-circle" OnClick="()=>{ context.Toggle();ValueChanged.InvokeAsync(color); }"
                     Width=Size Height=Size MinWidth=Size MinHeight=Size Color="@color">
                </MButton>
            </MCard>
        </MItem>
    }
</MItemGroup>

此时应用 ColorGroup 的代码变为如下代码,能够灵便的指定色彩组数据以及 ColorGroup 的 Class 和 Style 等:

<Masa.Blazor.Custom.Shared.Presets.ColorGroup Colors='new List<string>{"blue","green","yellow","red"}'>
</Masa.Blazor.Custom.Shared.Presets.ColorGroup>

启用隔离款式

第一局部开端提到了所有的 Class 除了 m -color-group 都是 Vuetify 提供的 class 款式, 那么 m -color-group 是哪来的?
新增 ColorGroup.Razor.css 文件,ColorGroup.Razor.css 文件内的 css 将被限定在 ColorGroup.Razor 组件内不会影响其它组件。最终会 ColorGroup.Razor.css 输入到一个名为 {ASSEMBLY NAME}.styles.css 的捆绑文件中,{ASSEMBLY NAME} 是我的项目的程序集名称。
本文示例并没有减少 ColorGroup.Razor.css,只是感觉作为封装组件现有款式够看了,减少m-color-group class 只是为了内部应用时不便 css 款式重写,并没有做任何定义。

更多隔离款式内容参考官网文档.

自定义插槽

目前为止,自定义的 ColorGroup 组件能够说曾经够看了,然而不够打。因为模式繁多,如果要在色彩抉择按钮后减少文本或者图片怎么办?这就又引入另外一个概念:插槽。
插槽(Slot)为 vue 中的叫法,Vuetify 组件提供了大量的插槽如文本输入框内的前后插槽和输入框外的前后插槽(默认为 Icon),MASA Blazor 同样实现了插槽的性能,这也使得咱们更容易定义和扩大本人的组件。

Blazor 面向 C# 开发者更违心称之为 Template 或者 Content,通过 RenderFragment 实现插槽的成果。
若你的组件须要定义子元素,为了捕捉子内容,须要定义一个名为 ChildContent 类型为 RenderFragment 的组件参数。

ColorGroup.Razor.cs 文件中减少 RenderFragment 属性来定义每项开端追加的插槽,并定义 string 参数,接管以后的色彩值。

[Parameter]
public RenderFragment<string>? ItemAppendContent {get; set;}

RenderFragment<T> 定义带参数组件,应用时默认通过 context 获取参数值。更多内容参考官网文档

ColorGroup.Razor 文件中定义插槽地位

<MItem>
    <MCard Class="elevation-0" Style="@($"transition: opacity .4s ease-in-out; {(context.Active ? "":"opacity: 0.5;")}")">
         <MButton Fab class="mx-1 rounded-circle" OnClick="()=>{ context.Toggle();ValueChanged.InvokeAsync(color); }" Width=Size Height=Size MinWidth=Size MinHeight=Size Color="@color">
         </MButton>
    </MCard>
    @if (ItemAppendContent is not null)
    {
        <div class="m-color-item-append d-flex align-center mr-1">
             @ItemAppendContent(color)
        </div>
    }
</MItem>

最终的成果如下:

组件优化

组件在保障性能和好看的同时,也要保障性能,以下只是列举了一些笔者认为比拟惯例的优化形式。

缩小组件从新渲染

正当重写 ShouldRender 办法,防止老本昂扬的从新出现。
贴一下官网代码自行领会,即肯定条件都合乎时才从新渲染:

@code {
    private int prevInboundFlightId = 0;
    private int prevOutboundFlightId = 0;
    private bool shouldRender;

    [Parameter]
    public FlightInfo? InboundFlight {get; set;}

    [Parameter]
    public FlightInfo? OutboundFlight {get; set;}

    protected override void OnParametersSet()
    {
        shouldRender = InboundFlight?.FlightId != prevInboundFlightId
            || OutboundFlight?.FlightId != prevOutboundFlightId;

        prevInboundFlightId = InboundFlight?.FlightId ?? 0;
        prevOutboundFlightId = OutboundFlight?.FlightId ?? 0;
    }

    protected override bool ShouldRender() => shouldRender;}

缩小不必要的 StateHasChanged 办法调用,默认状况下,组件继承自 ComponentBase,会在调用组件的事件处理程序后主动调用 StateHasChanged,对于某些事件处理程序可能不会批改组件状态的状况,应用程序能够利用 IHandleEvent 接口来管制 Blazor 事件处理的行为。示例代码见官网文档。

正当重写组件生命周期办法

首先要了解组件生命周期,特地是 OnInitialized(组件接管 SetParametersAsync 中的初始参数后调用)、OnParametersSet(接管到参数变更时调用)、OnAfterRender(组件实现出现后调用)。
以上办法每个都会执行两次及以上 (render-mode=”ServerPrerendered”)。
组件初始化的逻辑正当的调配到各个生命周期办法内,最常见的就是 OnAfterRender 办法内,firstRender 为 true 时调用 js 或者加载数据:

protected override async Task OnAfterRenderAsync(bool firstRender)
{if (firstRender)
    {
        await JS.InvokeVoidAsync("setElementText1", divElement, "Text after render");
    }
}

OnInitialized 生命周期:

  • 在动态预出现组件时执行一次。
  • 在建设服务器连贯后执行一次。
    防止双重出现行为,应传递一个标识符以在预出现期间缓存状态并在预出现后检索状态。

定义可重用的 RenderFragment

将反复的出现逻辑定义为 RenderFragment,无需每个组件开销即可重复使用出现逻辑。毛病就是重用 RenderFragment 短少组件边界,无奈独自刷新。

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {<p>Welcome to your new app!</p>};
}

防止为反复的元素从新创立委托

Blazor 中过多反复的创立 lambda 表达式委托可能会导致性能不佳,如对一个按钮组每个按钮的 OnClick 调配一个委托。能够将表达式委托改为 Action 缩小调配开销。

实现 IDisposable 或 IAsyncDisposable 接口

组件实现 IDisposable 或 IAsyncDisposable 接口,会在组件从 UI 中被删除时开释非托管资源,事件登记操作等。

组件不须要同时实现 IDisposable 和 IAsyncDisposable。如果两者均已实现,则框架仅执行异步重载。

更多内容参考:https://docs.microsoft.com/zh…

总结

这里只演示了一个 ColorGroup 很简略的例子,当然你也能够把这个组件做的足够“简单”, 其实组件的封装并没有设想的那么简单,无外乎下面提到的四个因素:界面、参数、款式、插槽。既然有些组件官网不提供,只能本人入手饥寒交迫(当然还是心愿官网提供更多规范组件之外的扩大组件)。

示例我的项目地址,更多内容参考 Masa Blazor 预置组件 实现。

开源地址

MASA.BuildingBlocks:https://github.com/masastack/…

MASA.Contrib:https://github.com/masastack/…

MASA.Utils:https://github.com/masastack/…

MASA.EShop:https://github.com/masalabs/M…

MASA.Blazor:https://github.com/BlazorComp…

如果你对咱们的 MASA Framework 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们

正文完
 0