轻松创建具有自动化功能的Blazor组件库,并同时支持 razor文件和 RenderTreeBuilder 两种方式。
- 属性优先,从参数中优雅地定义 CSS
- 轻松通过属性与组件关联
- 可自定义CSS和组件的属性编码逻辑
- 同时支持 razor文件 或
RenderTreeBuilder
来创建组件 - 支持具有类似参数的组件的“预定义”
- 动态且简单地和 JS 进行交互
- 拦截器设计,为组件的生命周期赋予新的功能
- 渲染器管道模式,以识别动态渲染组件
- 丰富地 'RenderTreeBuilder' 扩展方法,代码片段轻松编写
- 用 Fluent API 轻松创建元素和组件
- 在
Button.razor
@inherits BlazorComponentBase
<button @attributes="@GetAttributes()">
@ChildContent
</button>
@code{
[CssClass("btn")]
public Button()
{
}
[Parameter][CssClass("active")]public bool Active { get; set; }
[Parameter][CssClass("btn-")]public Color? Color { get; set; }
[Parameter]public RenderFragment? ChildContent { get; set; }
[Parameter][HtmlData("tooltip")]public string? Tooltip { get; set; }
[Parameter][HtmlEvent("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set; }
[Parameter][HtmlAttribute]public string? Title { get; set; }
public enum Color
{
Primary,
Secondary,
[CssClass("info")]Information,
}
}
- 在
Button.cs
类
[HtmlTag("button")]
[CssClass("btn")]
public class Button : BlazorComponentBase, IHasChildContent, IHasOnClick
{
[Parameter][CssClass("active")]public bool Active { get; set; }
[Parameter][CssClass("btn-")]public Color? Color { get; set; }
[Parameter]public RenderFragment? ChildContent { get; set; }
[Parameter][HtmlData("tooltip")]public string? Tooltip { get; set; }
[Parameter][HtmlEvent("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set;
[Parameter][HtmlAttribute]public string? Title { get; set; }
}
public enum Color
{
Primary,
Secondary,
[CssClass("info")]Information,
}
- 使用和对比
<!--razor-->
<Button Color="Color.Primary">Submit</Button>
<!--html-->
<button class="btn btn-primary">Submit</button>
<!--razor-->
<Button Active Tooltip="active button" Color="Color.Information" Title="click me">Active Button</Button>
<!--html-->
<button class="btn btn-info active" data-tooltip="active button" title="click me">Active Button</button>
- 导入模块
//in app.js
export function display(){
// ...your code
}
[Inject]IJSRuntime JS { get; set; }
var js = await JS.Value.ImportAsync("./app.js");
js.display(); // same as function name
- 动态运行 JS 代码
JS.Value.EvaluateAsync(window => {
window.console.log("log")
});
JS.Value.EvaludateAsync(@"
console.log(\"log\");
")
- 快速使用 JS 回调 C# 代码
JS.Value.InvokeVoidAsync("myFunction", CallbackFactory.Create<string>(arg=> {
//get arg from js
}));
JS.Value.InvokeVoidAsync("calculate", CallbackFactory.Create<int,int>((arg1,arg2)=> {
//get value of arg1,arg2 from js
}))
function myFunction(dotNetRef){
dotNetRef.invokeMethodAsync("Invoke", "arg");
}
function calculate(dotNetRef){
dotNetRef.invokeMethodAsync("Invoke", 1, 2);
}
- Logical CSS
protected override void BuildCssClass(ICssClassBuilder builder)
{
if(builder.Contains("annotation-enter"))
{
builder.Remove("annotation-exist");
}
else
{
builder.Append("annotation-enter").Append("annotation-exist");
}
}
- Logical Attributes
protected override void BuildAttributes(IDictionary<string, object> attributes)
{
attributes["onclick"] = HtmlHelper.Event.Create(this, ()=>{ ... });
if(attrbutes.ContainKey("data-toggle"))
{
attributes["data-toggle"] = "collapse";
}
}
builder.Div()
.Class("my-class")
.Class("active", IsActive)
.Class("text-block", !string.IsNullOrEmpty(Name))
.Style($"font-size:{Size}px", Size.HasValue)
.Content("hello world")
.Close();
builder.Component<Button>()
.Class("my-class")
.Class("active", IsActive)
.Class("text-block", !string.IsNullOrEmpty(Name))
.Style((Size.HasValue, $"font-size:{Size}px"))
.Content(ChildContent)
.Close();
builder.Ul().ForEach("li", result => {
result.attribute.Content($"content{result.index}");
});
List.razor
作为父组件
<ul @attributes="@GetAttributes()">
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
</ul>
ListItem.razor
作为子组件
<li @attributes="@GetAttributes()">@ChildContent</li>
@code{
[ChildComponent(typeof(List))]
public ListItem()
{
}
[CascadingParameter] public List CascadedList { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
}
List
组件类
[ParentComponent] //作为父组件
[HtmlTag("ul")]
public class List : BlazorComponentBase, IHasChildContent
{
}
ListItem
组件类
[ChildComponent(typeof(List))] //强关联
[HtmlTag("li")]
public class ListItem : BlazorComponentBase, IHasChildContent
{
[CascadingParameter]public List CascadedList { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
}
<List>
<ListItem>...</ListItem>
</List>
<ListItem /> <!--ListItem 组件不在 List 组件中将抛出异常-->
- 在
.razor
中
<div class="@GetCssClass"></div>
@code{
string GetCssClass => HtmlHelper.Class.Append("btn-primary").Append("active", Actived).ToString();
}
- 应用于 RenderTreeBuilder 时
builder.CreateElement(0, "span", attributes:
new {
@class = HtmlHelper.Class
.Append("btn-primary")
.Append("active", Actived),
style = HtmlHelper.Style.Append($"width:{Width}px"),
onclick = HtmlHelper.Event.Create<MouseEventArgs>(this, e=>{ //...click... });
});
您可以拦截组件的生命周期
- 定义拦截器
public class LogInterceptor : ComponentInterceptorBase
{
private readonly ILogger<LogInterceptor> _logger;
public LogInterceptor(ILogger<LogInterceptor> logger)
{
_logger = logger;
}
//在 SetParameterAsync 方法中执行
public override void InterceptSetParameters(IBlazorComponent component, ParameterView parameters)
{
foreach(var item in parameters)
{
_logger.LogDebug($"Key:{item.Name}, Value:{item.Value}");
}
}
}
- 注册拦截器
builder.Services.AddComponentBuilder(configure => {
configure.Interceptors.Add(new LogInterceptor());
})
识别特殊情况,以呈现指定的组件
public class NavLinkComponentRender : IComponentRender
{
public bool Render(IBlazorComponent component, RenderTreeBuilder builder)
{
if ( component is IHasNavLink navLink )
{
builder.OpenComponent<NavLink>(0);
builder.AddAttribute(1, nameof(NavLink.Match), navLink.Match);
builder.AddAttribute(2, nameof(NavLink.ActiveClass), navLink.ActiveCssClass);
builder.AddAttribute(3, nameof(NavLink.ChildContent), navLink.ChildContent);
builder.AddMultipleAttributes(4, component.GetAttributes());
builder.CloseComponent();
return false;
}
return true;
}
}
- 注册渲染器
builder.Services.AddComponentBuilder(configure => {
configure.Renderers.Add(typeof(NavLinkComponentRenderer));
});
- 从
Nuget.org
安装
Install-Package ComponentBuilder
- 注册服务
builder.Services.AddComponentBuilder();
使用 ComponentBuilder.Templates
生成组件库解决方案和在线演示站点
dotnet new install ComponentBuilder.Templates
dotnet new blazor-sln -n {YourRazorLibraryName}
更多信息见 templates