浏览器中的 .Net Core —— Blazor WebAssembly 初体验

前言

       在两年多之前就听闻 Blazor 框架,是 .Net 之父的业余实验性项目,其目的是探索 .Net 与 WebAssembly 的兼容性和应用前景。如今这个项目已经正式成为 Asp.Net Core 框架的一部分,公开了预览版,官方教程也基本写好上线了。就着这个机会,顺便体验一下这个框架用起来如何。html

       以前在网上搜索 Blazor 的相关信息的时候发现吵得很厉害。前端开发者大多以为有 Vue 之类的前端 MVVM 框架已经够用,没有 C# 插足的余地。甚至不少 C# 开发者也不知道这个框架的基本工做原理,以为是把 C# 翻译成 js,翻译以后就变成了相似 Vue 的东西。还有人以为这是下一个 Flash 或者 Silverlight。毛主席曾经说过:没有调查就没有发言权。对于说这种话的人,我只想说,少刷几分钟抖音快手随便搜下百度都能搞清楚怎么回事,做为 C# 开发者,这都理解不了我是真不知道是怎么学的 C#,难道真是传说中的拖控件一把梭,而后就没而后了?前端

       简单说明下 Blazor WebAssembly 的工做原理。就是在 WebAssembly 框架的基础上,实现了一个 .Net Core Runtime,用一个启动 js 下载相关 dll、初始化 .Net 虚拟机、启动虚拟机运行入口函数,接下来就和一个正常 .Net 程序同样,该怎么运行怎么运行。用 Java 的说法就是在浏览器中运行的 jvm。今后,.Net 跨平台领先 Java 一步,除了 Windows、Linux、MacOS以外,还要加上浏览器。悄悄说一下,浏览器上的运行时实现了 netstandard 2.1,待遇比传统的 .Net Framework 还好。要说缺点就是调试很麻烦,由于整个运行过程和服务器无关,在 VS 下断点也没用,不知道是预览版没作好仍是什么缘由,顺便致使出问题很难跟踪。还有改了代码要从新编译项目,不能像 MVC 那样改了 cshtml 刷新下浏览器就生效。每次重启调试太耐等了。git

正文

       目前 Blazor WebAssembly 还不是默认项目模板的一部分,须要自行下载模板才能在 VS 2019 的项目模板里找到,须要的能够移步官方教程。不知道有多少园友知道我有个专门收集各类各类我以为有趣的示例代码的项目,固然也有不少代码是我本身写的。我就冒出了一个想法,如何把这个项目也集成到个人现有项目中。毕竟建立独立的项目和在现有项目中融合新东西彻底是两种感受,不少组件一旦融合就会各类冲突打架,须要深刻了解他们才能知道冲突有没有办法解决,要如何解决。github

       通过几天的研究,我成功把 Blazor WebAssembly 项目融合进了个人主项目。同时进行了一些改造。主要方法仍是先新建一个模板项目,而后对比代码差别,融合代码,补充 nuget 包。接下来简要说明下在现有 Asp.Net Core 项目中增长 Blazor WebAssembly 项目的主要步骤。在个人项目中,/blazor 是 Blazor 根目录,各类修改都配合这个设定,各位请根据本身的状况修改。api

客户端

       一、新建一个包含 Asp.Net Core 宿主服务器的 Blazor WebAssembly 项目。纯 Blazor WebAssembly 项目发布后能够放到静态文件服务器,宿主服务器也只是当文件服务器用。把客户端项目复制到主项目解决方案中,在 VS 中添加现有项目。若是修改过项目名称和命名空间,请重启 VS,否则可能报错。浏览器

       二、复制共享项目到主项目的解决方案,修复项目引用。安全

       三、修改 wwwroot/index.html,修改 <head> 标签中的 <base href="/" /> 为 <base href="/blazor" />。服务器

       四、在 wwwroot 文件夹新建文件夹 blazor,把 wwwroot 下的其余文件和文件夹放进 wwwroot/blazor 文件夹,避免和主项目路径冲突,一样地,主项目也不能再用 /blazor/xxx 了。网络

服务端

       一、安装 nuget 包 Microsoft.AspNetCore.Blazor.Server,要勾上包括预发行版,否则搜不到。mvc

       二、在主项目中引用客户端项目和共享项目。

       三、在 Startup.ConfigureServices 中增长代码:

1 services.AddResponseCompression(opts =>
2 {
3     opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
4         new[] { "application/octet-stream" });
5 });

       四、在 Startup.Configure 中增长 app.UseBlazorDebugging(); 若是只想在开发环境使用,自行增长 if 判断。通常跟 app.UseDeveloperExceptionPage(); 放在一块儿。

       五、在 Startup.Configure 中注册 Blazor 文件,注意类型参数,是客户端项目的 Program 类:

app.UseClientSideBlazorFiles<BlazorApp.Client.Program>();

       六、在 Startup.Configure 中检查是否有并补充 app.UseStaticFiles();

       七、在 Startup.Configure 中注册路由终结点,注意 "/blazor/{**subPath}" 这一段,表示把 blazor 映射到 /blazor/xxx 去。{**subPath} 这一段是路由终结点参数捕获语法,里面的 subPath 能够乱写,但不能空着不填,否则启动不了。由于 Blazor WebAssembly 启动以后路由都是前端完成的,跟服务器没有任何关系,因此能够乱填。只是要知足系统的语法要求好让服务器能正常启动。

1 app.UseEndpoints(endpoints =>
2 {
3     //之前的 mvc、api 等等各类注册。
4 
5     //映射 Blazor 客户端终结点
6     endpoints.MapFallbackToClientSideBlazor<BlazorApp.Client.Program>("/blazor/{**subPath}", "index.html");
7 });

       至此,融合工做完成,能够正常启动项目并访问 /blazor 体验效果了。

效果预览

在两个浏览器(Chrome、Edge by Chromium)分别登陆不一样帐号,分别使用 Blazor WebAssembly SignalR .Net Core Client 和 SignalR Javascript Client 链接 SignalR 服务,手机(Edge Android)再登陆另外一个帐号使用 Blazor WebAssembly SignalR .Net Core Client 链接 SignalR 服务(域名是花生壳域名作 DDNS)。实现跨平台跨终端实时聊天。运行在 Release 发布模式。

 

 结语

       整体来讲,Blazor 的体验仍是很不错的,总体风格和 Asp.Net Core 几乎一摸同样,我看见的一瞬间就感受很是亲切。依赖注入系统也能够正常使用,只是区别是 Scope 生命周期的实际效果和单例是同样的,由于整个应用就只有一个 Scope,可是 Blazor Server Side 就有区别了,每一个 SignalR 链接绑定一个 Scope,掉线重连成功也会恢复到原先的 Scope ,一直连不上过久就不行了,整个服务器进程包含一个单例。因此在注册的时候尽可能用 Scope,避免单例成习惯,缓不过来。

       Razor 语法也是个神同样的设计,最初是做为 MVC3 的视图引擎推出,在 MVC4 成为默认引擎。好像 MVC5 取消了 aspx 视图引擎支持,.Net Core 完全取消了和 aspx 有关的一切东西。如今,Razor 又成为了一个前端渲染框架,真是老树发新芽,又是一春。在 html 模板渲染上,我用下来就是 Razor 的 @ 和 Vue 的双花括号语法特别顺手,aspx 那种尖括号语法实在看的头昏脑胀。原本 html 就各类尖括号,还要再来一堆尖括号,VS 高亮都看得头疼,更别说通常文本编辑器打开了,根本看不懂,到底谁和谁是一对?Razor 就特别爽,代码和标记自动识别切换,局部代码块,局部变量,比起 Vue 是有过之而无不及(Vue 的变量做用域师从 js,是真的晕)。

       根据目前使用的状况来看,只要不包含涉及系统底层调用的库均可以正常使用,好比和进程、线程、硬件驱动相关、本机 dll 互操做这种。

       我在模板项目中,增长了 SignalR 客户端使用示例。也是根据官方教程修改,并且使用的客户端库就是普通 .Net 客户端库,和控制台、桌面程序用是同一套 dll。微软果真神,究竟是怎么把网络相关的 API 底层实现神不知鬼不觉地的换掉的。默认注入的 HttpClient 也是 System.Net.Http 命名空间的。

       因为网络通讯底层其实是依赖浏览器,因此浏览器会自动把 HttpClient 的请求嫁接到浏览器上,相关的 Headers、Cookies 天然也会自动携带上。因此若是 Blazor 应用和普通网页在同一个域,这些东西实际上会共享。个人身份认证相关功能就是利用这个特色偷懒实现的。若是不在同一个域,最简单实用的方法就是用 IdentityServer4 做为认证服务,客户端引用 nuget 包 IdentityModel,这个包会给 HttpClient 增长一堆用来和 OpenId Connect、OAuth2.0 协议交互的扩展方法,看成两个程序用开放协议配合工做来写就行。用 IdentityModel 扩展获取 Access Token,请求的时候把 Token 加进 HttpClient 的 Headers,固然也能够用 IdentityModel 的扩展来注入,更方便。

       对于 SPA 应用来讲,状态管理必定是没法绕过的,不过在 Blazor 中,直接用依赖注入来管理状态就能够了。若是须要刷新页面也不丢失状态的话,能够考虑使用 ILocalStorage 服务来持久化状态,或者其余持久化方案也行,反正支持 js 互操做,先随便封装一个应急,等 C# 的原生组件出来了再看怎么办。

       与服务器的交互除了常规的 Web Api,还有内部预览阶段的 gRPC-Web,等这东西搞定了,Blazor 极限使用一切二进制数据,那效率不知道能提高多少。Asp.Net Core 3.0 全面支持 HTTP2,Chrome 好像从 70 日后也都支持 HTTP2,gRPC-Web 原生使用 HTTP2 确定比如今包一层兼容层支持 HTTP1.1 来的好。能够说在浏览器的限制下,能作的应该都差很少。不知道之后浏览器会不会开放线程接口让 WebAssembly 使用内核线程执行计算密集型任务(好像会更方便黑客把浏览器当矿机啊,开放的问题也是多的不行,感受浏览器就是个黑暗森林,网站服务器要防用户搞破坏,用户也要防网站用脚本搞破坏,这个猜疑链也致使浏览器各类限制,难啊)。

        做为一个杂食性开发者,对于 Blazor 与 Vue 的争论这种东西我是无所谓的,只要在个人知识范围内在我能接受的开发复杂度内解决问题,其余的都是浮云。就像邓爷爷说的:实践是检验真理的惟一标准;管他黑猫白猫,抓到耗子就是好猫;这才是个人信条,完全的实用主义。

       啊,C# 这种强类型安全语言进入前端领域有点激动,不注意就说了一大通。被 js 那诡异的对象类型,动态做用域坑的实在是不行,敲键盘的时候心虚不知道会不会莫名其妙忽然就出问题,实在是对心脏很差。仍是喜欢 C#,什么东西都清晰明了,不埋暗坑,外加 DLR 和 dynamic,真是进可攻、退可守。js 彻底没有退路,实在是伤不起。

       在最后发布之后才发现 Blazor 聊天进不去,调试正常,试了N多办法都没搞定,最后删除发布文件夹中的全部 dll,关闭 VS,删除 obj、bin 文件夹,从新发布才正常。真是坑爹。

 

       转载请完整保留如下内容并在显眼位置标注,未经受权删除如下内容进行转载盗用的,保留追究法律责任的权利!

  本文地址:http://www.javashuo.com/article/p-hqftgsla-bp.html

  完整源代码:Github

  里面有各类小东西,这只是其中之一,不嫌弃的话能够Star一下。

相关文章
相关标签/搜索