不知道是否有人跟我一样想在浏览器上直接可以动态的编译blazor
的一些组件库?而不是通过引用NuGet
以后才能查看到效果,并且在使用别人的组件的时候可以在动态的调整组件的一些样式
不说了开始正文:
本文我们将使用Masa
提供的一个组件实现动态编译github.com直通车 ,执行环境将在WebAssembly
中执行,为什么使用WebAssembly
而不是Server
呢?首先我们需要先了解这俩种模式的执行原理
WebAssembly:
Blazor WebAssembly
是Blazor WebAssembly
,用于使用 .NET 生成交互式客户端 Web 应用。Blazor WebAssembly
使用无插件或将代码重新编译为其他语言的开放式 Web 标准。Blazor WebAssembly
适用于所有新式 Web 浏览器,包括移动浏览器。
Server:
-
Blazor Server
在ASP.NET Core
应用中支持在服务器上托管 Razor 组件。 可通过 SignalR 连接处理 UI 更新。运行时停留在服务器上并处理:
- 执行应用的 C# 代码。
- 将 UI 事件从浏览器发送到服务器。
- 将 UI 更新应用于服务器发送回的已呈现的组件。
由于编译是完全可操作的,存在安全问题,在Server的模式下用户编译的环境就是服务器的环境,这样用户就可以通过动态编译代码实现操作侵入安全,问题很严重,如果有心人使用对于安全影响过于严重,不建议在Server中使用动态编译
实现我们来创建一个空的WebAssembly项目
mkdir compileRazor
cd compileRazor
dotnet new blazorwasm-empty
使用vs打开项目添加Masa.Blazor.Extensions.Languages.Razor ,将一下代码添加到项目文件中
<PackageReference Include="Masa.Blazor.Extensions.Languages.Razor" Version="0.0.1" />
修改Program.cs
文件的代码
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using compileRazor;
using Masa.Blazor.Extensions.Languages.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
var app = builder.Build();
// 初始化RazorCompile
RazorCompile.Initialized(await GetReference(app.Services), await GetRazorExtension());
await app.RunAsync();
// 添加程序集引用
async Task<List<PortableExecutableReference>?> GetReference(IServiceProvider services)
{
#region WebAsembly
// need to add Service
var httpClient = services.GetService<HttpClient>();
var portableExecutableReferences = new List<PortableExecutableReference>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
//你需要通过网络获取程序集,应为无法通过程序集目录获取
var stream = await httpClient!.GetStreamAsync($"_framework/{assembly.GetName().Name}.dll");
if (stream.Length > 0)
{
portableExecutableReferences?.Add(MetadataReference.CreateFromStream(stream));
}
}
catch (Exception e) // There may be a 404
{
Console.WriteLine(e.Message);
}
}
#endregion
// 由于WebAssembly和Server返回portableexecutablerreference机制不同,需要分开处理
return portableExecutableReferences;
}
async Task<List<RazorExtension>> GetRazorExtension()
{
var razorExtension = new List<RazorExtension>();
foreach (var asm in typeof(Program).Assembly.GetReferencedAssemblies())
{
razorExtension.Add(new AssemblyExtension(asm.FullName, AppDomain.CurrentDomain.Load(asm.FullName)));
}
return razorExtension;
}
修改Pages\Index.razor
的代码
@page "/"
@using Masa.Blazor.Extensions.Languages.Razor;
<button class="button" @onclick="Run">刷新</button>
<div class="input-container">
<textarea @bind="Code" type="text" class="input-box" placeholder="请输入执行代码" >
</textarea>
</div>
@if (ComponentType != null)
{
<DynamicComponent Type="ComponentType"></DynamicComponent>
}
@code{
private string Code = @"<body>
<div id='app'>
<header>
<h1>Doctor Who™ Episode Database</h1>
</header>
<nav>
<a href='main-list'>Main Episode List</a>
<a href='search'>Search</a>
<a href='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: 0 auto;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.input-box {
width: 100%;
height: 100px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
}
</style>
首次编译会比较慢,在WebAssembly
下还可以因为电脑问题造成卡顿,如果是需要提供开发效率可以使用Server调试,在Server调试的话是比WebAssembly
快很多,而且WebAssembly
还没有做Aot,性能不会太好
来自token的分享
技术交流群:737776595
推荐一个超级好用的Blazor UI
组件 MASA Blazor 开源协议 MIT
商用完全没问题