本篇博客只实现基本的低代码,比如新增组件,动态修改组件参数
创建项目
首先创建一个空的Blazor Server,并且命名LowCode.Web
实现我们还需要引用一个Blazor组件库,由于作者用Masa Blazor比较多所以使用Masa Blazor
安装Masa Blazor
将Masa Blazor添加到项目依赖中
<ItemGroup> <PackageReference Include="Masa.Blazor" Version="1.0.0-preview.3" /> </ItemGroup>
修改Program.cs文件 增加了builder.Services.AddMasaBlazor();
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddMasaBlazor(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.Run();
打开_Imports.razor 添加以下代码
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using Microsoft.JSInterop @using LowCode.Web @using Masa.Blazor @using BlazorComponent @using LowCode.Web.Components
修改Pages_Host.cshtml,添加以下代码
@page "/" @using Microsoft.AspNetCore.Components.Web @namespace LowCode.Web.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <base href="~/"/> <link href="css/site.css" rel="stylesheet"/> <!-- masa blazor css style --> <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet"/> <!--icon file,import need to use--> <link href="https://cdn.masastack.com/npm/@("@mdi")/font@5.x/css/materialdesignicons.min.css" rel="stylesheet"> <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/> </head> <body> <component type="typeof(App)" render-mode="ServerPrerendered"/> <div id="blazor-error-ui"> <environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment> <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.server.js"></script> <!--js(should lay the end of file)--> <script src="_content/BlazorComponent/js/blazor-component.js"></script> </body> </html>
修改MainLayout.razor文件
@inherits LayoutComponentBase <MApp> @Body </MApp>
这样就完成安装了Masa Blazor
然后开始写实现
实现低代码组件设计
创建Components文件夹
创建渲染组件ComponentRender.razor,添加以下代码
@using LowCode.Web.Components.Options @RenderFragment @code{ /// <summary> /// 渲染组件 /// </summary> [Parameter] public RenderFragment RenderFragment { get; set; } /// <summary> /// 渲染配置 /// </summary> public GenerateMButtonOptions GenerateMButtonOptions { get; set; } /// <summary> /// 渲染动态代码 /// </summary> public string Code { get; set; } }
定义组件库模板 DynamicComponentGenerator.razor,由于cs文件不能razor模板,所以创建razor文件,添加以下代码,以下代码我们添加三个组件模板,
@using LowCode.Web.Components.Options @code { public static (RenderFragment, string) GenerateMButton(GenerateMButtonOptions options) { // 定义模板默认数据 RenderFragment template = @<MButton Block=options.block Height=options.height Width=options.width Class=@options.Class Style=@options.Style Dark=options.dark Attributes=options.attributes>@options.childContent</MButton>; // 模板定义代码 (存在问题) var data = $@"<MButton Block={options.block} Height={options.height} Width={options.width} Class={options.Class} Style={options.Style} Dark={options.dark} Attributes={options.attributes}>{@options.childContent}</MButton>"; return (builder => { builder.AddContent(0, template); }, data); } public static (RenderFragment, string) GenerateMCard(GenerateMButtonOptions options) { RenderFragment template = @<MCard Height=options.height Width=options.width Class=@options.Class Style=@options.Style Dark=options.dark Attributes=options.attributes>@options.childContent</MCard>; var data = $@"<MCard Height={options.height} Width={options.width} Class={options.Class} Style={options.Style} Dark={options.dark} Attributes={options.attributes}>{@options.childContent}</MCard>"; return (builder => { builder.AddContent(0, template); }, data); } public static (RenderFragment, string) GenerateMAvatar(GenerateMButtonOptions options) { RenderFragment template = @<MAvatar Height=options.height Width=options.width Class=@options.Class Style=@options.Style Attributes=options.attributes>@options.childContent</MAvatar>; var data = $@"<MAvatar Height={options.height} Width={options.width} Class={options.Class} Style={options.Style} Attributes={options.attributes}>{@options.childContent}</MAvatar>"; return (builder => { builder.AddContent(0, template); }, data); } }
添加ComponentComponentType.cs 组件枚举
namespace LowCode.Web.Components; public enum ComponentType { MButton, MCart, MAvatar }
添加ComponentComponentLibrary.razor用于显示支持的组件
<div style="height: 100px"> <MButton class="button" OnClick="() => OnClick?.Invoke(ComponentType.MButton)"> <MIcon> mdi-card </MIcon> 按钮 </MButton> <MButton class="button" OnClick="() => OnClick?.Invoke(ComponentType.MCart)"> <MIcon>mdi-id-card</MIcon> 卡片 </MButton> <MButton class="button" OnClick="() => OnClick?.Invoke(ComponentType.MAvatar)"> <MIcon>mdi-id-card</MIcon> 头像 </MButton> </div> @code{ public delegate void OnClickDelegate(ComponentType type); [Parameter] public OnClickDelegate? OnClick { get; set; } } <style> .button { margin: 5px; } </style>
新增ComponentOptionsGenerateMButtonOptions.cs 添加以下代码 ,添加组件时的参数
using BlazorComponent; using Microsoft.AspNetCore.Components; namespace LowCode.Web.Components.Options; public class GenerateMButtonOptions { public string? height { get; set; } public string? width { get; set; } public bool block { get; set; } public bool dark { get; set; } public string Style { get; set; } = string.Empty; public string Class { get; set; } = string.Empty; public Dictionary<string, object>? attributes { get; set; } = new(); public RenderFragment? childContent { get; set; } }
然后修改PagesIndex.razor,
@page "/" @using LowCode.Web.Components.Options <MRow NoGutters> <MCol> <MCard Class="pa-1" Outlined Style="height: 100vh" tile> <ComponentLibrary OnClick="CreateOnClick"></ComponentLibrary> </MCard> </MCol> <MCol Order="2" Cols="12" Sm="6" Md="8"> <MCard Class="pa-2" Outlined Style="height: 100vh" tile> @foreach (var item in Renders) { <render @onclick="() => Id = item.Key"> @item.Value.RenderFragment </render> } </MCard> </MCol> <MCol Order="3"> <MCard Class="pa-2" Outlined Style="height:100vh" tile> <MCard> @*TODO:通过反射实现获取组件参数根据参数类型显示指定组件动态修改参数*@ @foreach (var item in Renders) { var options = item.Value.GenerateMButtonOptions; if (item.Key == Id) { <MTextField @bind-Value="options.width" Label="width"></MTextField> <MTextField @bind-Value="options.height" Label="height"></MTextField> <MTextField @bind-Value="options.Style" Label="Style"></MTextField> <MTextField @bind-Value="options.Class" Label="Class"></MTextField> <MDivider></MDivider> <MButton OnClick="() => AddOptionsAttribute(options.attributes)" Block>新增扩展参数输入框</MButton> @foreach (var e in options.attributes) { <MTextarea NoResize Rows="1" Value="@e.Key" ValueChanged="(v) => { options.attributes.Remove(e.Key);options.attributes.Add(v,e.Value);}"></MTextarea> <MTextarea NoResize Rows="1" Value="@options.attributes[e.Key].ToString()" ValueChanged="(v)=>options.attributes[e.Key]= v"></MTextarea> } <MButton Block OnClick="()=>DeleteComponent(item.Key)">删除</MButton> } } </MCard> </MCard> </MCol> </MRow> @code { public string Id { get; set; } private Dictionary<string, Render> Renders = new(); private RenderFragment RenderFragment { get; set; } private void AddOptionsAttribute(Dictionary<string, object> attribute) { attribute.Add("new",""); } private void DeleteComponent(string key) { Renders.Remove(key); } private void CreateOnClick(ComponentType type) { GenerateMButtonOptions options = null; string code; switch (type) { case ComponentType.MButton: options = new() { childContent = @<span>新建的按钮</span>, attributes = new Dictionary<string, object>(), width = "100px", }; (RenderFragment, code) = DynamicComponentGenerator.GenerateMButton(options); Renders.Add(Guid.NewGuid().ToString("N"), new Render() { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code }); break; case ComponentType.MCart: options = new() { childContent = @<MButton>多个按钮</MButton>, attributes = new Dictionary<string, object>(), width = "100px", }; (RenderFragment, code) = DynamicComponentGenerator.GenerateMCard(options); Renders.Add(Guid.NewGuid().ToString("N"), new Render() { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code }); break; case ComponentType.MAvatar: options = new() { childContent = @<MImage Src="https://cdn.masastack.com/stack/images/website/masa-blazor/jack.png" Alt="Jack"></MImage>, attributes = new Dictionary<string, object>(), width = "100px", }; (RenderFragment, code) = DynamicComponentGenerator.GenerateMAvatar(options); Renders.Add(Guid.NewGuid().ToString("N"), new Render() { RenderFragment = RenderFragment, GenerateMButtonOptions = options, Code = code }); break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } StateHasChanged(); } }
这样就实现了整个简单的低代码操作,我们可以使用看看效果,简单了解实现原理
我们定义了组件的模板,这个模板是固定的,通过Blazor提供的双向绑定实现动态修改组件参数,这种方式可能不太适合完整的低代码,但是也是不错的思路,

这个项目还处于Demo阶段,不知道是否有大佬一块研究,研究技术极限,Blazor非常好用,推荐
GitHub
项目是MIT开源,希望大佬一块学习,促进Blazor生态
来着Token的分享