一.唠唠WebAssembly的发展历程
目前有很多支持WebAssembly的项目,但发展最快的是Blazor,这是一个构建单页面的.NET技术,目前已经从Preview版本升级到了beta版本,微软计划在2020年5月发布Blazor的第一个版本。
Blazor是什么?它是一项将C#和.NET都放入浏览器的Microsoft技术。它使用WebAssembly来工作,WebAssembly是一种高性能的管道,可以将代码预编译为紧凑的二进制格式。最重要的是,每个主流浏览器(包括移动版本)都支持WebAssembly。
十年前,JavaScript统治世界还不是很明显。Flash和Silverlight也正在运行。这二个都需要使用浏览器插件来完成工作,并且都以不同的用户界面方法替换了HTML。这种方法使他们在功能方面遥遥领先于JavaScript,但随着移动互联网的出现,他们就慢慢过时。
但随后从最初的Javascript再到微软的JScript和CEnvi的ScriptEase三足鼎立,再到最后的统一标准,当时微软凭借Windows系统捆绑Internet Explorer的先天优势击溃Netscape后,两大巨头就此进入了长达数年的静默期,JavaScript就是在这样的情况下被构想出来的,当时的浏览器之王,Netscape Navigator创始人Marc Andreessen认为Netscape需要一种“glue language”来支持HTML,让Web设计师和兼职程序员可以很容易地使用它来组装诸如图像和插件之类的组件,且代码是可以直接写在网页标记中。除此之外微软的步步紧逼也迫使Andreessen不得不聘请Brendan Eich,及早将Scheme编程语言嵌入到Netscape Navigator中。1995年,JavaScript以Mocha为名开发,并于9月在Netscape Navigator 2.0的测试版中首次发布,当时被称为LiveScript,12月,在Netscape Navigator 2.0 beta 3中部署时被重命名为JavaScript 。虽然Netscape Navigator在Chrome、Internet Explorer和Firefox等多款浏览器的围追堵截中最终落败,但是JavaScript却推动了网页的发展,并一直被沿用至今。
这是一个讽刺。在JavaScript征服世界的同时,播下了一颗很小的种子,这可能会在将来的某个时候暗示JavaScript的终结。那颗种子是名为asm.js的实验技术。
这是Mozilla的开发人员在2013年完成的一个古怪的实验。他们正在寻找在浏览器中运行高性能代码的方法。但是与插件不同,asm.js并未尝试在浏览器旁边运行。相反,它的目的是直接通过Javascript的虚拟化。
从本质上讲,asm.js是简洁,优化的JavaScript语法。它比普通的JavaScript运行得更快,因为它避免了该语言的慢动态部分。但是认识到它的网络浏览器也可以应用其他优化,从而大大提高性能。换句话说,asm.js遵循黄金法则- 不要破坏网络 -同时提供通往未来改进的途径。Firefox团队使用asm.js以及名为Emscripten的转码工具来获取用C ++构建的实时3D游戏,并将其放入Web浏览器中,并且仅在JavaScript和原始野心上运行。
有人问为什么asm.js好在哪里,简单而言,它的性能比JavaScript高几百倍,当然是在没有谷歌的V8引擎之下,因为JavaScript是弱类型语言,它需要猜测你的数据类型来进行编译,这样的情况下,在我看来它肯定需要遍历完一个方法,然后再进行运算,与其这样我为什么不打个标识呢?当然在不破坏JavaScript的情况下,arm.js选择了一个骚气的想法,如果你想你的数据类型是int,那么声明一个值就变成了变量名|0,就这样它的目的就达到了。
尽管asm.js实验产生了一些令人眼花撩乱的演示,但工作的开发人员基本上忽略了它。对他们来说,这只是超越现代的一个有趣方面。但这随着WebAssembly的创建而改变。
WebAssembly既是asm.js的后继产品,又是一项截然不同的技术。这是一种紧凑的二进制代码格式。像asm.js一样,WebAssembly代码也被输入到JavaScript执行环境中。它具有相同的沙箱和相同的运行时环境。与asm.js一样,WebAssembly的编译方式也可以提高效率。但是现在,这些效率比有以前更加明显,并且浏览器可以完全跳过JavaScript解析阶段。对于普通的逻辑,WebAssembly远比常规JavaScript快,几乎与本机编译的代码一样快。
WebAssembly于2015年首次出现。如今,桌面和移动设备上的四大浏览器(Chrome,Edge,Safari和Firefox)已完全支持它。尽管可以通过将WebAssembly代码转换为asm.js来实现向后兼容,但Internet Explorer不支持它。就让IE凉透吧!但需要注意的是WebAssembly无法回避JavaScript,因为它已锁定在JavaScript运行时环境中。实际上,WebAssembly需要与至少一些普通的JavaScript代码一起运行,因为它不能直接访问页面。这意味着如果不通过JavaScript层,就无法操纵DOM或接收事件。
听我说起来,这是一个限制,但聪明的微软开发者已经找到了走私的方法,在浏览器中下载一个微型.NET运行时,作为已编译的WASM文件。此运行时处理JavaScript互操作,并提供基本服务,它能给我们提供GC或者其它用法。Blazor不是唯一一个由WebAssembly支持的实验。考虑一下Pyodide,它旨在将Python放入浏览器中,并带有用于数据分析的高级数学工具包。据我所知这应该使用emscripten的编译器。
人们常说,何时Javascript能够替代服务器端语言,又有人说什么时候可以代替桌面级应用程序,所以WebAssembly并不是用来代替JavaScript的。而是为了解决现代问题,如果它做到了,那就真的做到了!所以作为一个程序员,你应该对WebAssembly引起足够的重视,未来快速加载Web应用程序的需求肯定会增加。
就现在我们的.NET Core提供了两种Blazor模板,包括Blazor Server 以及 Blazor WebAssembly。
Blazor Server是一种具有一些有趣用例的技术,但是由于不断的通信,您显然会牺牲一些性能-甚至不用问脱机功能。Blazor WebAssembly是受到最多宣传的一种,也是我们在本文中探讨的一种。
关于Blazor,程序员最常见的误解是将其C#代码编译为WebAssembly,然后发送到浏览器,然后执行。这种方法并非不可能-Blazor的创建者暗示他们将来可能会尝试这种技术。但是如今Blazor的工作方式并不是如此。
换句话说,如今的Blazor是当您访问使用Blazor的网页时,该页面将从下载按比例缩小的.NET运行时开始。然后它将下载您的应用程序以及您的应用程序使用的任何其他.NET库,所有这些都在其本机IL中。最后,Blazor运行时执行IL。
二.配置您的开发环境
由于Blazor是一个预发布的早期Beta产品。基础结构的关键部分正在发生变化,您将无法获得与其他类型的Microsoft项目相同级别的工具支持。我尝试在Visual Studio 2019中进行编码,需要注意的是您需要勾选.NET FrameWork 4.8 以及 .NET Core 3.0 + ,这样您才具有Web Assembly的项目。完成设置后,您可以轻松创建Blazor项目。只需启动Visual Studio,创建一个新项目,然后选择“ Blazor App”项目即可。Visual Studio会询问您是否需要Blazor Server应用程序或Blazor WebAssembly应用程序.
三.Blazor的数据绑定与组件传值
由于关于Blazor的.NET Core 又一杀器! Web Blazor框架横空出世!一篇我编写的文章,未能提及更深入的内容,那么现在我将要介绍一下高级的Blazor用法,到最后还会有一个糖果,园友力作的Blazor UI!多么激动人心的时刻,那么赶快开始吧.
3.1 Child Component
在Blazor的Child Component中可以使用[Parameter] 关键字,来进行传值的定义,我们可以这么来做,现在只是提一下这个概念,下面会仔细说下组件之间如何进行跨组件绑定值。
<div> <p>标题:@title</p> </div> @code{ [Parameter] public string title { get; set; } }
随后在调用时,Visual Studio IDE 就可以直接向您的视觉进行提示输入相关属性。
<Demorazor title="Hello 博客园的兄弟们!"></Demorazor>
运行效果如下:
3.2 single Bind and Two-way binding
single bind就不用说了,新建项目自带的模板Counter示例那就是如此。
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
此处 @currentCount 值根据点击按钮的数量递增Click me。<p>标记元素中的值会自动刷新,无需任何组件刷新。
two-way binding 我们可以自定义我们的事件 一共分为二中绑定方式 包括@bind 和 @Bind-Value,值得一提的是还可以通过使用event参数指定@bind-value属性, 使用其他事件来绑定属性或字段。例如第四个文本框就是绑定changeString采用oninput事件的属性,以到达在文本框的值更改时激发,经过我的测试如果你的绑定事件是Javascript中不存在的,那么也无妨,不会报出系统级别的异常,我想如果是从IL转换到WebAssembly中,就会直接过滤掉,但是Visual Studio 2019 没有给我们提示,也让我们编译通过,即使是当前的最高16.0.4 预览版也是如此,这个是令我诧异的。
<p> <span>在这里可以使用bind-value 或者 bind 当然这里确保您不使用其它事件!</span> <input @bind-value="changeString" /> <p>这是我输入的内容: @changeString</p> </p> <p> <span>oninput</span> <input @bind-value="changeString" @bind-value:event="oninput" /> </p> @code { string changeString = ""; }
运行效果如下:
3.3 Component bindings
想要跨组件进行绑定属性值,可以使用,@bind-{property}可在其中跨组件绑定属性值,我们试着尝试,首先我们创建一个子控件,这个blazor就叫Baby,有一个身份证Id的属性和出生地址。
EventCallback的用法非常广泛,它可以跨组件共享方法和属性,如不写下面的两个属性,则就会报错。
@page "/baby" <h2>Child Compoent</h2> <p>出生的Baby IdentityCard:@Baby_IdentityCrad_Id</p> <h3>在{@Baby_new_Address} 生的</h3> @code { [Parameter] public string Baby_IdentityCrad_Id{ get; set; } /// <summary> /// 这个属性也是牛的雅皮~~~ hhh /// </summary> [Parameter] public string Baby_new_Address{ get; set; } [Parameter] public EventCallback<string> Baby_IdentityCrad_IdChanged { get; set; } [Parameter] public EventCallback<string> Baby_new_AddressChanged { get; set; } }
有什么样的儿子就会有什么样的爸爸? 现在我们创建出父亲,那就直接叫做一个Father.razor吧~
@page "/father" <h3>Father</h3> <Baby @bind-Baby_IdentityCrad_Id="@id_Card" @bind-Baby_new_Address="@address"> </Baby> <button class="btn btn-primary" @onclick="@ChangeTheYear">new baby()</button> @code { public string id_Card { get; set; } public string address { get; set; } private void ChangeTheYear() { id_Card = Guid.NewGuid().ToString(); address = "老张"; } }
运行效果如下:
如果要在子组件中定义事件,则可以MouseEventArgs来接受设备上的事件,然后再进行附加事件。
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
四.级联传值
在某些情况下, 使用组件参数将数据从祖先组件流式传输到附属组件是不方便的, 尤其是在有多个组件层时。 级联值和参数通过提供一种方便的方法, 使上级组件为其所有子代组件提供值。 级联值和参数还提供了一种方法来协调组件。我们试着去构建一个例子,首先创建一个最顶层的组件。
@page "/myDome" <p><span>姓名:</span><input @bind="@pName" /></p> <p><span>年龄:</span><input @bind-value="@pAge" @bind-value:event="oninput"/></p> <CascadingValue Value="@pName" Name="ProfileName"> <CascadingValue Value="@pAge" Name="ProfileAge"> <ParentComponent /> </CascadingValue> </CascadingValue> @code { private string pName { get; set; } = "张三"; private int pAge { get; set; } = 35; }
ParentComponent.razor:
<div style="background-color:darkgray;width:200px;"> <p>Parent Component</p> <div style="padding:10px;"> <p> 年龄 :@Age</p> <ChildComponent /> </div> </div> @code{ [CascadingParameter(Name = "ProfileAge")] int Age { get; set; } }
ChildComponent.razor:
<div style="background-color:beige;width:200px;"> <p>Child Component</p> <p>名称 : @Name.ToString()</p> </div> @code{ [CascadingParameter(Name = "ProfileName")] string Name { get; set; } }
运行效果如下:
可以发现,一级直接将二级和三级的组件进行了数据穿透,不过需要注意的是CascadingValue的Name一定要和CascadingParameter的Name相同,否则将会执行错误。
五.路由
从古至今,任何大型的开发框架,都是具有路由的,否则可能将会无法工作,其实Blazor的启动页也就使用了路由,这是毋庸置疑的。当你的组件带有 @page
指令时,将为生成的类指定 RouteAttribute 指定路由模板的。 在运行时,路由器将使用 RouteAttribute
查找组件类,并呈现哪个组件包含与请求的 URL 匹配的路由模板。
@page "/luyou" @page "/luyou/{text}" <h1>Blazor is @Text!</h1> @code { [Parameter] public string Text { get; set; } protected override void OnInitialized() { Text = Text ?? "fantastic"; } }
运行效果如下:
在上面的示例中应用了两个 @page
指令。 第一个允许导航到没有参数的组件。 第二个 @page
指令采用 {text}
路由参数,并将该值分配给 Text
属性。
关于Blazor的基础入门咱们这篇就说到这里,相信你一定觉得Blazor了不起!它是一个现代的开源框架。它也由一家拥有悠久历史的公司拥有,该公司放弃了昨天的闪亮新技术。因此,大多数开发人员都应该谨慎对待Blazor。只要JavaScript能够执行Blazor可以做的所有事情,而没有下载大小,性能和新工具堆栈带来的额外挑战,大多数开发人员将一如既往。
这并不意味着Blazor不能在所有这些领域都占有一席之地。它甚至可能成为.NET Web应用程序开发中的主导力量。但是如果我今天必须下注,这就是我要依靠的东西。WebAssembly是未来。但就目前而言,Blazor只是一种有趣的可能性。
六.彩蛋
就现在!我的好朋友宇辰正在开发一款名为Blazui的UI组件。它为什么叫Blazui?
Blazor + Element UI = Blazui,Element UI 的blazor版本,无JS,无TS,用 .Net 写前端的 UI 框架,非 Silverlight,非 WebForm,开箱即用!!
Blazui 演示地址:http://blazui.com:9000。QQ群:74522853,码云地址:https://gitee.com/wzxinchen/blazui
参考Blazor使用的前提条件:
现在Blazor正在逐渐变好,让我们即刻出发!.NET Core 不只是开源!