blazor
I don't know if anyone wants to dynamically compile some component libraries directly on the browser like me ? Instead of NuGet
viewing the effect after reference, and when using other people's components, you can dynamically adjust some styles of the components
Not to mention the beginning of the text:
In this article, we will use Masa
a component provided to implement dynamic compilation of github.com through-train , and the execution environment will WebAssembly
be executed in it. Why WebAssembly
not use Server
it? First of all, we need to understand the execution principles of these two modes
WebAssembly:
Blazor WebAssembly
YesBlazor WebAssembly
, for building interactive client-side web applications using .NET.Blazor WebAssembly
Use open web standards without plug-ins or recompiling code into other languages.Blazor WebAssembly
Works with all modern web browsers, including mobile browsers.
Server:
-
Blazor Server
Hosting Razor components on the server is supported inASP.NET Core
the app . UI updates can be handled over a SignalR connection.The runtime stays on the server and processes:
- Execute the application's C# code.
- Send UI events from browser to server.
- Applies UI updates to rendered components sent back by the server.
Since the compilation is completely operable, there are security issues. In the Server mode, the user's compilation environment is the server environment, so that the user can dynamically compile the code to achieve operational intrusion security. The problem is very serious. Seriously, it is not recommended to use dynamic compilation in Server
Implementation Let's create an empty WebAssembly project
mkdir compileRazor
cd compileRazor
dotnet new blazorwasm-empty
Use vs to open the project and add Masa.Blazor.Extensions.Languages.Razor , add the following code to the project file
<PackageReference Include="Masa.Blazor.Extensions.Languages.Razor" Version="0.0.1" />
Modify Program.cs
the code of the file
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;
}
modified Pages\Index.razor
code
@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>
Then the effect of starting the program is as shown in the figure:
The first compilation will be slower, WebAssembly
and it may be stuck due to computer problems. If you need to improve development efficiency, you can use Server debugging. Debugging on Server is WebAssembly
much faster than that, and WebAssembly
it has not done Aot, so the performance will not be very good.
Sharing from token
Technical exchange group: 737776595
Recommend a super easy-to-use Blazor UI
component MASA Blazor open source protocol, MIT
commercial use is no problem at all