翼度科技»论坛 编程开发 .net 查看内容

不用Blazor WebAssembly,开发在浏览器端编译和运行C#代码的网站

8

主题

8

帖子

24

积分

新手上路

Rank: 1

积分
24
本文中,我将会为大家分享一个如何用.NET技术开发“在浏览器端编译和运行C#代码的工具”,核心的技术就是用C#编写不依赖于Blazor框架的WebAssembly以及Roslyn技术。
一、 为什么要开发这样的工具?
对于编程初学者来讲,开发环境的安装配置是一个令人头疼的事情,如果能让初学者不用做任何的安装配置,直接打开浏览器就能编写、运行代码,那么这将会大大降低编程初学者的学习门槛。
目前已经有一些可以在线编写、运行C#代码的网站了,这些网站的实现思路有如下两种:
思路1把代码从前端提交到在后端服务器上,然后在服务器上进行编译、运行,然后把运行结果再显示到前端。这样做的缺点是无法完成复杂的输入输出、界面交互等。
思路2用Mono技术编写WebAssembly。这样做的缺点是对于C#语法的跟进不及时,一些新的C#语法不被支持。
因此,开发一个能在浏览器端编译运行C#代码,并且支持最新C#语法的工具就很重要了。要开发这样的工具,WebAssembly是一个绕不过去的技术。
二、 什么是WebAssembly
传统的前端开发都是使用JavaScript来编写逻辑,而WebAssembly让我们可以用其他编程序言编写在浏览器中运行的程序。由于WebAssembly属于现代浏览器的标准,所以在浏览器中运行WebAssembly程序并不需要安装额外的插件。现在Java、Go、Python等主流的编程语言都已经支持编译为WebAssembly。
三、 Blazor WebAssembly的缺点
 .NET 中的Blazor WebAssembly技术可以把C#代码编译为WebAssembly运行在浏览器端。但是传统的Blazor WebAssembly是一个侵入性很强的框架,也就是整个系统都必须使用C#技术进行开发,而不能选择只是其中一个组件使用C#代码,其他地方仍然使用传统的JavaScript进行开发。当然,通过Microsoft.AspNetCore.Components.CustomElements,我们可以只把界面的一小块使用C#进行开发,但是这种方式仍然是“在页面上留一个用C#写的区域”,非常的重量级,而不能实现“只用C#写一个函数”这样轻量级的组件,也就是用C#写一个非侵入性、依赖性很低的轻量级WebAssembly组件。
四、 不用Blazor WebAssembly,用.NET技术开发WebAssembly
从.NET 6开始,我们可以使用C#编写轻量级的WebAssembly,生成的WebAssembly只需要使用Blazor提供的基础运行环境,而不需要引入整个Blazor WebAssembly技术。
                下面,我将会通过一个简单的“用C#计算两个数的和”的例子来演示这个技术的用法。当然,这只是一个简单的演示,实际项目中肯定不会用C#完成这么简单的功能。下面的项目用.NET 7进行演示,其他版本使用可能会略有不同。
1、 创建一个.NET普通类库项目,然后通过Nuget安装如下两个组件:Microsoft.AspNetCore.Components.WebAssembly、Microsoft.AspNetCore.Components.WebAssembly.DevServer,然后把类库项目的csproj文件的根节点中Sdk属性的值修改为"Microsoft.NET.Sdk.BlazorWebAssembly"。
修改后的文件类似如代码 1所示。
 
代码 1  csproj项目文件
  1. <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
  2.   <PropertyGroup>
  3.     <TargetFramework>net7.0</TargetFramework>
  4.     <ImplicitUsings>enable</ImplicitUsings>
  5.     <Nullable>enable</Nullable>
  6.   </PropertyGroup>
  7.   <ItemGroup>
  8.     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.2" />
  9.     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.2" />
  10.   </ItemGroup>
  11. </Project>
复制代码
 
2、 在类库项目中创建一个文件Program.cs,内容如代码 2所示。
 
代码 2 Program.cs
  1. using Microsoft.JSInterop;
  2. namespace Demo1
  3. {
  4.     public class Program
  5.     {
  6.         private static async Task Main(string[] args)
  7.         {
  8.         }
  9.         [JSInvokable]
  10.         public static int Add(int i,int j)
  11.         {
  12.             return i + j;
  13.         }
  14.     }
  15. }
复制代码
 
                这里Main方法目前是空的,但是不能被省略。Add方法上的[JSInvokable]表示这个方法可以被JavaScript调用,也就是这个方法属于一个可以被调用的Web Assembly方法。
3、 编译项目,生成文件夹下的wwwroot文件夹中的_framework文件夹中就是生成的Web Assembly和相关文件。
4、 用任何你喜欢的前端技术创建一个前端项目。我这里不使用任何的前端框架,而是直接用普通的HTML+Javascript来编写前端项目。
首先,我们要把上一步生成的_framework文件夹复制到前端项目的根文件夹下。
然后,我们编写index.html文件,内容如代码 3所示。
代码 3 index.html
  1. <html lang="en">
  2.     <head>
  3.         <meta charset="UTF-8" />
  4.     </head>
  5.     <body></body>   
  6.         
  7.    
  8. </html>
复制代码
 
                接下来解释一下上面的代码,用来引入相关的文件。Blazor.start();用来启动Blazor运行时环境; DotNet.invokeMethodAsync用来调用WebAssembly中的方法,第一个参数为被调用的程序集的名字,第二个参数为被调用的方法的名字,之后的参数为给被调用的方法传递的参数值。
                可以看到,这里我们就是把WebAssembly当成一个组件在用,完全不对页面有其他特殊的要求。所以这个组件可以在任何前端框架中使用,也可以和其他前端的库一起使用。
                最后,我们运行这个前端项目. 由于Blazor会生成blat、dll等不被Web服务器默认接受的文件类型,所以请确保在Web服务器上为如下格式的文件配置MimeType:.pdb、.blat、.dat、.dll、.json、.wasm、.woff、.woff2。我这里测试用的Web服务器是IIS,所以在网站根文件夹下创建如所示的Web.config文件即可,如代码 4所示,使用其他Web服务器的开发者请参考所使用的Web服务器的手册进行MimeType的配置。
代码 4 Web.config
  1. <html lang="en">
  2.     <head>
  3.         <meta charset="UTF-8" />
  4.     </head>
  5.     <body></body>   
  6.         
  7.    
  8. </html><html lang="en">
  9.     <head>
  10.         <meta charset="UTF-8" />
  11.     </head>
  12.     <body></body>   
  13.         
  14.    
  15. </html>                        
复制代码
5、 在浏览器端访问Web服务器中的index.html,如果看到如Figure 1所示的弹窗,就说明Javascript成功了调用了C#编写的Add方法。
 

Figure 1 程序执行弹窗

五、 C#编写WebAssembly的应用场景
C#编写的WebAssembly默认占的流量比较大,大约要占到30MB。我们可以通过BlazorLazyLoad、启用Brotli算法等方式把流量降到5MB以下,具体用法请网上搜索相关资料。
在我看来,用C#编写WebAssembly有包含但不限于如下的场景。
场景1复用一些.NET组件或者C#代码。这些已经存在的.NET组件或者C#代码虽然也能用Javascript重写,但是这样增加了额外的工作量。而通过WebAssembly就可以直接重用这些组件。比如,我曾经在后端开发用到过一个PE文件解析的Nuget包,这个包采用的.NET Standard标准开发,而且全部是在内存中进行文件内容的处理,因此我就可以直接在WebAssembly中继续使用这个包在前端对PE文件进行处理。
场景2使用一些WebAssembly组件。因为C/C++等语言编写的程序可以移植为WebAssembly版本,因此很多经典的C/C++开发的软件也可以继续在前端使用。比如音视频处理的ffmpeg已经有了WebAssembly版本,因此我们就可以用C#调用它进行音视频的处理;再比如,著名的计算机视觉库OpenCV也被移植到了WebAssembly中,因此我们也可以使用C#在前端进行图像识别、图像处理等操作。WebAssembly非常适合开发“在线图像处理、在线音视频、在线游戏”等工具类应用的开发。
场景3开发一些复杂度高的前端组件。我们知道,在开发复杂度高的项目的时候,Javascript经常是力不从心,即使是Typescript也并不会比Javascript有更根本性的改善。相比起来,C#等更适合工程化的开发,因此一些复杂度非常高的前端组件用C#编写为WebAssembly有可能更合适。
上面提到的这些场景下,我们可以只把部分组件用C#开发为WebAssembly,其他部分以及项目整体仍然可以继续用Javascript进行开发,这样各个语言可以发挥各自的特色。
六、 C#回调JavaScript中的方法
在编写WebAssembly的时候,我们可能需要在C#中调用Javascript中的方法。我们可以通过IJSRuntime、IJSInProcessRuntime分别调用Javascript中的异步方法和同步方法。
再创建一个类库项目Demo2,首先按照上面第四节中对项目进行配置,不再赘述。
index.html和Program.cs的代码如代码 5和代码 6所示。
代码 5 index.html
  1. <html lang="en">
  2. <head>
  3.     <meta charset="UTF-8" />
  4. </head>
  5. <body>
  6.    
  7.         
  8.         <input type="text" id="txtValue" />
  9.         <button id="btnClose">close</button>
  10.    
  11.     <ul id="ulMsgs">        
  12.     </ul>
  13. </body>
  14. </html>
复制代码
 
代码 6 Program.cs

[code]using Microsoft.AspNetCore.Components.WebAssembly.Hosting;using Microsoft.JSInterop;namespace Demo2;public class Program{    private static IJSRuntime js;    private static async Task Main(string[] args)    {        var builder = WebAssemblyHostBuilder.CreateDefault(args);        var host = builder.Build();        js = host.Services.GetRequiredService();        await host.RunAsync();    }    [JSInvokable]    public static async Task Count()    {        string strCount = await js.InvokeAsync("showMessage","Input Count");        for(int i=0;i

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具