不知道是否有人跟我一样想在浏览器上直接可以动态的编译`blazor`的一些组件库?而不是通过引用`NuGet`以后才能查看到效果,并且在使用别人的组件的时候可以在动态的调整组件的一些样式
不说了开始正文:
本文我们将使用`Masa`提供的一个组件实现动态编译[github.com直通车](https://github.com/BlazorComponent/MASA.Blazor.Extensions/tree/main/src/Languages/Masa.Blazor.Extensions.Languages.Razor) ,执行环境将在`WebAssembly`中执行,为什么使用`WebAssembly`而不是`Server`呢?首先我们需要先了解这俩种模式的执行原理
## WebAssembly:
- `Blazor WebAssembly` 是`Blazor WebAssembly`,用于使用 .NET 生成交互式客户端 Web 应用。 `Blazor WebAssembly` 使用无插件或将代码重新编译为其他语言的开放式 Web 标准。 `Blazor WebAssembly` 适用于所有新式 Web 浏览器,包括移动浏览器。
## Server:
- `Blazor Server`在 `ASP.NET Core` 应用中支持在服务器上托管 Razor 组件。 可通过 [SignalR](https://learn.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-7.0) 连接处理 UI 更新。
运行时停留在服务器上并处理:
- 执行应用的 C# 代码。
- 将 UI 事件从浏览器发送到服务器。
- 将 UI 更新应用于服务器发送回的已呈现的组件。
由于编译是完全可操作的,存在安全问题,在Server的模式下用户编译的环境就是服务器的环境,这样用户就可以通过动态编译代码实现操作侵入安全,问题很严重,如果有心人使用对于安全影响过于严重,不建议在Server中使用动态编译
实现我们来创建一个空的WebAssembly项目
mkdir compileRazor cd compileRazor dotnet new blazorwasm-empty
使用vs打开项目添加[Masa.Blazor.Extensions.Languages.Razor](https://github.com/BlazorComponent/MASA.Blazor.Extensions/tree/main/src/Languages/Masa.Blazor.Extensions.Languages.Razor) ,将一下代码添加到项目文件中
<PackageReferenceInclude="Masa.Blazor.Extensions.Languages.Razor"Version="0.0.1"/>
修改`Program.cs`文件的代码
usingMicrosoft.AspNetCore.Components.Web; usingMicrosoft.AspNetCore.Components.WebAssembly.Hosting; usingcompileRazor; usingMasa.Blazor.Extensions.Languages.Razor; usingMicrosoft.AspNetCore.Razor.Language; usingMicrosoft.CodeAnalysis; varbuilder=WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after"); builder.Services.AddScoped(sp=>newHttpClient { BaseAddress=newUri(builder.HostEnvironment.BaseAddress) }); varapp=builder.Build(); // 初始化RazorCompileRazorCompile.Initialized(awaitGetReference(app.Services), awaitGetRazorExtension()); awaitapp.RunAsync(); // 添加程序集引用asyncTask<List<PortableExecutableReference>?>GetReference(IServiceProviderservices) { #regionWebAsembly// need to add ServicevarhttpClient=services.GetService<HttpClient>(); varportableExecutableReferences=newList<PortableExecutableReference>(); foreach (varassemblyinAppDomain.CurrentDomain.GetAssemblies()) { try { //你需要通过网络获取程序集,应为无法通过程序集目录获取varstream=awaithttpClient!.GetStreamAsync($"_framework/{assembly.GetName().Name}.dll"); if (stream.Length>0) { portableExecutableReferences?.Add(MetadataReference.CreateFromStream(stream)); } } catch (Exceptione) // There may be a 404 { Console.WriteLine(e.Message); } } #endregion// 由于WebAssembly和Server返回portableexecutablerreference机制不同,需要分开处理returnportableExecutableReferences; } asyncTask<List<RazorExtension>>GetRazorExtension() { varrazorExtension=newList<RazorExtension>(); foreach (varasmintypeof(Program).Assembly.GetReferencedAssemblies()) { razorExtension.Add(newAssemblyExtension(asm.FullName, AppDomain.CurrentDomain.Load(asm.FullName))); } returnrazorExtension; }
修改`Pages\Index.razor`的代码
@page "/" @using Masa.Blazor.Extensions.Languages.Razor; <buttonclass="button"@onclick="Run">刷新</button><divclass="input-container"><textarea@bind="Code"type="text"class="input-box"placeholder="请输入执行代码"></textarea></div>@if (ComponentType != null) { <DynamicComponentType="ComponentType"></DynamicComponent>} @code{ private string Code = @"<body><divid='app'><header><h1>Doctor Who™ Episode Database</h1></header><nav><ahref='main-list'>Main Episode List</a><ahref='search'>Search</a><ahref='new'>Add Episode</a></nav><h2>Episodes</h2><ul><li>...</li><li>...</li><li>...</li></ul><footer> Doctor Who is a registered trademark of the BBC. https://www.doctorwho.tv/ </footer></div></body>"; private Type? ComponentType; private void Run() { ComponentType = RazorCompile.CompileToType(new CompileRazorOptions() { Code = Code // TODO: 在WebAssembly下保证ConcurrentBuild是false 因为Webassembly不支持多线程 }); StateHasChanged(); } } <style>.button{ width: 100%; font-size: 22px; background-color: cornflowerblue; border: 0px; margin: 5px; border-radius: 5px; height: 40px; } .input-container { width: 500px; margin: 0auto; padding: 10px; border: 1pxsolid#ccc; border-radius: 5px; } .input-box { width: 100%; height: 100px; border: 1pxsolid#ccc; border-radius: 5px; font-size: 14px; } </style>
然后启动程序效果如图:
首次编译会比较慢,在`WebAssembly`下还可以因为电脑问题造成卡顿,如果是需要提供开发效率可以使用Server调试,在Server调试的话是比`WebAssembly`快很多,而且`WebAssembly`还没有做Aot,性能不会太好
来自token的分享
技术交流群:737776595
推荐一个超级好用的`Blazor UI`组件 Masa Blazor 开源协议 `MIT` 商用完全没问题