dotnet 将C#编译为wasm让前端html使用

devtools/2024/10/15 20:18:49/

其实 dotnet 是全栈的首选,原因是因为可以开发的方向太多,比如大本营PC端,以及后台。还有移动端,包括 IOS 和安卓端。现在还能用来写前端,本文就来告诉大家如何在前端使用现有的C#代码,通过 WebAssembly 使用 C# 的代码支持完全静态的网页,也就是不需要任何后台的存在。同时使用 C# 编写的 WebAssembly 可以省去 js 编译时间,同时使用二进制的本地指令,运行效率也有极大的提升。兼顾了开发的友好以及更高的性能

这需要搜 WebAssembly 就可以找到超级多的赞扬的文章,我这里也就不需要多说了。接下来告诉大家使用一个超级简单的代码入门

使用 WebAssmebly 的方式不会影响原有的任何业务,也就是我在已经写了几年的页面里面,可以直接加入 WebAssmembly 的特性,就像多添加一个 js 引用一样。不需要对现有的页面做任何的改动

此时在 C# 里面用的代码都是虚的,不再本文关注的范围内,所以通过 dotnet new console -o YadernawcoLofeleabe 创建一个控制台项目

在控制台项目添加一个类,这个类添加静态方法,这个静态方法就是让前端调用的入口方法,给这个字符串添加字符串参数,方便传入

using System;namespace YadernawcoLofeleabe
{class Program{static void Main(string[] args){Console.WriteLine("Hello World!");}}public class Example{public static string Hello(string yourName){return $"Hello {yourName}";}}
} 

这里的代码不是重点,大概就是从 Hello 拿到输入,然后修改输入然后输出

接下来就是重点了,如何将 C# 代码编译为 WebAssmebly 了

这里的 C# 需要通过 mono 的辅助用于将 IL 转换为 WebAssembly 的代码,所以需要在Mono官网下载最新的 Mono 的 SDK 安装

点击下载

默认的 Mono 将会安装到 c:Program FilesMonobin 文件夹,如果是下载 x86 的就会安装到 c:Program Files(x86)Monobin 文件夹

然后下载 mono 在 wasm 的运行时,请 点击下载 将下载的 zip 文件夹解压缩到本地的文件夹,同时记住这个文件夹,如我将 zip 文件夹解压缩到 f:/lindexi/mono 文件夹

此时准备环境工作就完成了,下一步就是命令行编译了。当然这些步骤都是最基础的步骤,也有封装好的命令,也就是 dotnet wasm xx.csproj 完成编译,不过这一步需要先安装工具(注意这个工具还没正式发布)

通过 csc 命令将 C# 代码编译为 IL 文件。打开 VisualStudio 开发者命令行工具,进入到刚才创建的 Program.cs 所在文件夹

csc /target:library -out:Example.dll /noconfig /nostdlib /r:f:/lindexi/mono/wasm-bcl/wasm/mscorlib.dll /r:f:/lindexi/mono/wasm-bcl/wasm/System.dll /r:f:/lindexi/mono/wasm-bcl/wasm/System.Core.dll /r:f:/lindexi/mono/wasm-bcl/wasm/Facades/netstandard.dll /r:f:/lindexi/mono/wasm-bcl/wasm/System.Net.Http.dll /r:f:/lindexi/mono/framework/WebAssembly.Bindings.dll /r:f:/lindexi/mono/framework/WebAssembly.Net.Http.dll Program.cs

注意将 f:/lindexi/mono 文件夹替换为你刚才解压缩的 mono 运行时所在的文件夹

上面的代码通过引用 mono 运行时的库,将 Program.cs 文件编译为 Example.dll 文件

当然这里的 Example.dll 文件现在还是 IL 文件,还需要通过 mono 再次编译为 wasm 文件。注意这里说的编译为 wasm 并不是真的将 IL 编译 wasm 文件,而是编译为运行在 wasm 的 .NET 运行时可解析的文件。上面这句话已经过时,只是我逗比看文档理解不对,其实上面这一步编译的 IL 文件已经可以在 wasm 执行了。原因是在 wasm 会先运行一个 .NET 的运行时,由 .NET 运行时执行这个 IL 文件

单独一个 Example.dll 文件是不能直接运行的,如上面所说,需要添加一个.NET运行时。但是一个 .NET 运行时是超级大的,难道要用户每次打开网页都下载一个这么大的运行时?此时就需要用到 packager.exe 工具,通过这个工具,可以只添加引用的同时支持在 wasm 运行的库

"c:Program FilesMonobinmono" "f:/lindexi/mono/packager.exe" --copy=always --out=./publish Example.dll

注意上面的路径,如果安装的是 x86 的 mono 那么需要修改路径为 c:Program Files(x86)Monobinmono 此外需要将 f:/lindexi/mono/packager.exe 替换为你解压缩的 mono 运行时文件夹

执行上面命令如果看到下面输出,那么就是运行成功

cp: Always - f:tempWpfApp1YadernawcoLofeleabeExample.dll -> ./publishmanagedExample.dll
cp: Always - f:lindeximonowasm-bclwasmmscorlib.dll -> ./publishmanagedmscorlib.dll
cp: Always - f:lindeximonoframeworkWebAssembly.Bindings.dll -> ./publishmanagedWebAssembly.Bindings.dll
cp: Always - f:lindeximonowasm-bclwasmFacadesnetstandard.dll -> ./publishmanagednetstandard.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.dll -> ./publishmanagedSystem.dll
cp: Always - f:lindeximonowasm-bclwasmMono.Security.dll -> ./publishmanagedMono.Security.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Xml.dll -> ./publishmanagedSystem.Xml.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Numerics.dll -> ./publishmanagedSystem.Numerics.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Core.dll -> ./publishmanagedSystem.Core.dll
cp: Always - f:lindeximonoframeworkWebAssembly.Net.WebSockets.dll -> ./publishmanagedWebAssembly.Net.WebSockets.dll
cp: Always - f:lindeximonowasm-bclwasmFacadesSystem.Memory.dll -> ./publishmanagedSystem.Memory.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Data.dll -> ./publishmanagedSystem.Data.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Transactions.dll -> ./publishmanagedSystem.Transactions.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Data.DataSetExtensions.dll -> ./publishmanagedSystem.Data.DataSetExtensions.dll
cp: Always - f:lindeximonowasm-bclwasmFacadesSystem.Drawing.Common.dll -> ./publishmanagedSystem.Drawing.Common.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.IO.Compression.dll -> ./publishmanagedSystem.IO.Compression.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.IO.Compression.FileSystem.dll -> ./publishmanagedSystem.IO.Compression.FileSystem.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.ComponentModel.Composition.dll -> ./publishmanagedSystem.ComponentModel.Composition.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Net.Http.dll -> ./publishmanagedSystem.Net.Http.dll
cp: Always - f:lindeximonoframeworkWebAssembly.Net.Http.dll -> ./publishmanagedWebAssembly.Net.Http.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Runtime.Serialization.dll -> ./publishmanagedSystem.Runtime.Serialization.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.ServiceModel.Internals.dll -> ./publishmanagedSystem.ServiceModel.Internals.dll
cp: Always - f:lindeximonowasm-bclwasmSystem.Xml.Linq.dll -> ./publishmanagedSystem.Xml.Linq.dll

此时打开 Program.cs 所在的文件夹,可以看到文件夹包含了 publish 文件夹,这个文件夹里面的内容就是 wasm 使用的文件了,而刚才编译的 Example.dll 就放在 managed 文件夹里面

下一步就是如何在 html 中使用刚才编译出来的 Excample.dll 文件了,这部分感谢前端的小智的协助

需要在 html 中引用 publish 文件夹下的 mono-config.js 和 runtime.js 和 dotnet.js 文件夹

	<script type="text/javascript" src="./mono-config.js"></script><script type="text/javascript" src="./runtime.js"></script><script async type="text/javascript" src="./dotnet.js"></script>	

接下来就是如何在 js 代码调用 C# 编译的 dll 了

通过 Module.mono_bind_static_method 可以将 js 的一个方法绑定到一个静态的方法里面

Module.mono_bind_static_method("[Example] YadernawcoLofeleabe.Example:Hello");

使用格式是 Module.mono_bind_static_method("[dll文件名] 命名空间.类名:静态方法"); 如上面代码

尝试复制下面代码放在 html 里面

        <script type="text/javascript">let that = this;var App = {onClick: function () {that.output.value = "Please wait";that.output.value = that.execute("Ali");},init: function () {that.execute = Module.mono_bind_static_method("[Example] YadernawcoLofeleabe.Example:Hello");that.output = document.getElementById("output");that.button = document.getElementById("button");that.button.disabled = false;}};		</script>

如果你的 dll 命名和命名空间和我不相同,那么请自己修改

接下来就是添加简单的界面了

<!DOCTYPE doctype html>
<html lang="en"><head><!-- Required meta tags --><meta charset="utf-8"><meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"><!-- Bootstrap CSS --><link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" rel="stylesheet"><title>Hello, Mono WASM!</title></link></meta></meta></head><body><div class="container"><h1>Hello, world!</h1><form><div class="form-group"><label for="output">Output from C#:</label><textarea class="form-control" id="output" rows="10"></textarea></div><div class="form-group"><button class="btn btn-primary" id="button" onclick="App.onClick" type="button">Run WASM, Run!</button></div></form></div><script type="text/javascript">let that = this;var App = {onClick: function () {that.output.value = "Please wait";that.output.value = that.execute("Ali");},init: function () {that.execute = Module.mono_bind_static_method("[Example] YadernawcoLofeleabe.Example:Hello");that.output = document.getElementById("output");that.button = document.getElementById("button");that.button.disabled = false;}};		document.getElementById("button").addEventListener("click", App.onClick);document.body.addEventListener("load", App.init);</script><script src="./mono-config.js" type="text/javascript"></script><script src="./runtime.js" type="text/javascript"></script><script async="" src="./dotnet.js" type="text/javascript"></script></body>
</html>

尝试开启一个静态的 HTTP 服务器,然后在浏览器访问这个 html 文件,注意将 dll 文件设置用户可下载,这样就完成了。例子可以访问https://0x414c49.github.io/wasm-example/index.html 这里有所有的文件

其实我在入门翻了车,多谢下面大佬的博客,本文大部分代码都是抄下面博客

Run C# natively in the browser through the web assembly via mono-wasm

看到这里小伙伴想到了什么?没错,微软 Blazor 就是用这个原理,用 C# 写前端


http://www.ppmy.cn/devtools/4601.html

相关文章

Jmeter 场景测试:登录--上传--下载--登出

为了练习Jmeter的使用&#xff0c;今天我要测试的场景是“登录--上传--下载--登出”这样一个过程. 测试的目标是我曾经练手写的一个文件分享系统&#xff0c;它要求用户只有登录后才可以下载想要的文件。 Jmeter总体结构&#xff1a; 第一步&#xff1a;添加HTTP Cookie管理器…

计算机视觉动作识别——YOWO用于实时时空动作定位与识别的算法解析

摘要 时空动作定位要求将两种信息源整合到设计的架构中&#xff1a;(1) 来自先前帧的时间信息和(2) 来自关键帧的空间信息。当前的最先进方法通常使用单独的网络提取这些信息&#xff0c;并使用额外的机制进行融合以获得检测结果。YOWO是一个用于视频流中实时时空动作定位的统…

C++入门

目录 C入门 1. C关键字 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C输入&输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C支持函数重载的原理--名字修饰(name Mangling) 6. 引用 6.1 引用概念 6.2 引用特…

FastGPT+ChatGLM3本地部署

FastGPTChatGLM本地部署 本地部署硬性要求&#xff1a;显存13g以上 关于环境的安装就不多赘述&#xff0c;conda pip 可以解决大部分问题 ChatGLM本地运行 m3e-basechatglm3-6b 在huggingface上可以下载上述模型&#xff0c;如果没有梯子可以使用huggingface镜像 从git…

Axure中继器排序失效 /没变化解决

问题复现 通过设置交互条件后&#xff0c;但是没效果&#xff0c;查了很多资料&#xff0c;按照教程操作&#xff0c;仍旧没效果。 原因 结论先行&#xff1a;问题出在汉化包&#xff0c;你用了汉化包导致axure内部出错。最简单的办法&#xff0c;删除汉化文件&#xff0c;…

第七章 案例一:分析ARP解析过程

1、实验环境 主机A和主机B连接到交换机&#xff0c;并与一台路由器互连&#xff0c;如图7.17所示&#xff0c;路由器充当网关。 图7.17 实验案例一示意图 2、需求描述 查看 ARP 相关信息,熟悉在PC 和 Cisco 设备上的常用命令,设置主机A和主机B为同一个网段网关设置为路由接…

Webservice使用

Webservice使用教程 Webservice的交互模式是一个类似于CS结构的模式&#xff0c;因此它需要一个Server端与一个Client端。在Client端访问Server端的接口来实现Webservice的功能。 Server端 打开IDEA创建gradle模块 webservice-01-server1 然后再build.gradle.kts文件中添加以…

Day 14 网络协议

常见网络设备&#xff1a;交换机 路由器 中继器 多协议网关&#xff08;路由器的前身&#xff09; 交换机&#xff1a;用于连接统一网络的设备&#xff0c;实现内网设备通信。 从广义上分为&#xff1a;局域网交换机&#xff0c;广域网交换机 从网络构成分为&#xff1a;接…