开包即食的教程带你浅尝最新开源的C# Web引擎Blazor
在今年年初,恰逢新春佳节临近的时候。微软给全球的C#开发者们,着实的送上了一分惊喜。微软正式开源Blazor,将.NET带回到浏览器。
这个小惊喜,迅速的在dotnet开发者中间传开了。2018年3月22日Blazor发布了它的第一次Release Blazor到底是个什么样的东西呢?我们是否真的可以携着C#语言进入前端的市场中? 不如现在就跟我一起体验dotnet blazor吧。
一、前言
获取最新版的dotnet core 并安装Blazor模板:
- 安装 最新的.Net Core(版本需要高于2.1.101);
- 对于简单的尝试来说,VS code 已经足够。所以笔者并没有亲自安装Visual Studio;
使用命令行初始化项目:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates dotnet new blazor -o BlazorApp1 cd BlazorApp1 dotnet run
- 如果你需要使用Visual Studio:
- 安装最新的Visual Studio 2017;
- 安装 ASP.NET Core Blazor Language Services extension;
- 在Visual Studio中创建新的测试项目;
- 选择 File -> New Project -> Web -> ASP.NET Core Web Application;
- 确定在Target Framework里选择了 .NET Core and ASP.NET Core 2.0;
- 选择 Blazor 模板;
二、如何在前端渲染cshtml
当我们运行起项目之后,就可以看到如下提示:
这个时候我们在浏览器里打开监听的端口 http://localhost:17477,就可以看到我们这个项目的网页了。
这个简单的示例项目带了3个页面:
第一个页面比较简单,但先别急,让我们打开浏览器工具,先看看页面在加载页面过程中都加载了什么:
在初次打开页面的时候,我们看到的是这样一个Loading..的页面,这个页面的代码是这样的:
<!DOCTYPE html> <html> <head><meta charset="utf-8" /><title>BlazorDemo</title><base href="/" /><link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /><link href="css/site.css" rel="stylesheet" /> </head> <body><app>Loading...</app><script src="css/bootstrap/bootstrap-native.min.js"></script><script src="_framework/blazor.js" main="BlazorDemo.dll" entrypoint="BlazorDemo.Program::Main" references="Microsoft.AspNetCore.Blazor.Browser.dll,Microsoft.AspNetCore.Blazor.dll,Microsoft.Extensions.DependencyInjection.Abstractions.dll,Microsoft.Extensions.DependencyInjection.dll,mscorlib.dll,netstandard.dll,System.Core.dll,System.Diagnostics.StackTrace.dll,System.dll,System.Globalization.Extensions.dll,System.Net.Http.dll,System.Runtime.Serialization.Primitives.dll,System.Security.Cryptography.Algorithms.dll" linker-enabled="true"></script></body> </html>
可以看到这个页面加载了两个js,第一个是bootstrap的,第二个叫做blazor.js。只不过这个js有非常多的参数,有 main、entrypoint和references。看看References里写的是不是很熟悉? 一看就是.net的dll。
blazor.js加载了mono.js,mono.js加载了mono.wasm。这个是个什么文件?
wasm代表的就是Web Assembly,简单地说它就是编译好的二进制文件,可以由浏览器直接运行,源语言可以是C/C++或者任何可以编译到Web Assembly的文件,而这里我们加载的就是mono编译好的Web Assembly文件,它被加载之后,相当于浏览器中启动了一个mono 运行环境。
随后的两个js是笔者chrome浏览器插入的js,在这里不要被他们干扰了。那么mono 运行时加载完成之后,就需要加载dotnet 的Dll了,首先是入口库,接着就是需要的引用库。
好家伙 1.9MB!当所有的Dll被下载完毕之后,这个时候我们的浏览器就可以运行我们这个dotnet的网页了。于是就回到了我们最开始看到的那个应用程序。
所以,总结一下blazor.js调用mono.js,mono.js加载mono.wsam,然后根据写在script标签里的内容继续的加载dotnet的库文件。如果浏览器不支持wsam,就会尝试使用asm.js加载mono.asm.js。
三、Blazor的模板究竟是怎样的?
我们已经知道,经过前面的步骤,浏览器里已经运行了一个.Net 运行时了。而且加载了项目必须的dll。那么这样一个简单的程序,它的代码究竟是怎么样的呢?
打开项目代码,映入眼帘的是一个标准的.net Project:
_ViewImports.cshtml包含了项目一些其他页面中最常使用的namespace:
@using System.Net.Http @using Microsoft.AspNetCore.Blazor @using Microsoft.AspNetCore.Blazor.Components @using Microsoft.AspNetCore.Blazor.Layouts @using Microsoft.AspNetCore.Blazor.Routing @using BlazorDemo @using BlazorDemo.Shared
Program.cs是程序的入口点:
using Microsoft.AspNetCore.Blazor.Browser.Rendering; using Microsoft.AspNetCore.Blazor.Browser.Services; using System;namespace BlazorDemo {class Program{static void Main(string[] args){var serviceProvider = new BrowserServiceProvider(configure =>{// Add any custom services here });new BrowserRenderer(serviceProvider).AddComponent<App>("app");}} }
在入口点中,我们注册了一个浏览器渲染服务 BrowserRender,让他渲染App。
App.cshmtl是这样的:
<Router AppAssembly=typeof(Program).Assembly />
这里的Router对应的是Microsoft.AspNetCore.Blazor.Routing.Router。当给它一个AppAssembly时,他就会自动的把当前的Url 和 AppAssembly的其他Pages对应起来。
所以,当我们在浏览器里输入 /Counter时,他就会加载Pages/Couter.cshtml。
Shared文件夹里分别是布局文件、导航栏,还有一个我们自定义的控件 SurveyPrompt。
熟悉Razor引擎的小伙伴们一定很轻车熟路了。那么当我们打开网站时,默认显示给我们的就是Index,这个时候我们会加载Pages/Index.cshtml。
Index.cshtml的代码是这个样子的:
@page "/"<h1>Hello, world!</h1> Welcome to your new app. <SurveyPrompt Title="How is Blazor working for you?" />
@page 可以告诉Router,当前页面注册到 "/"。
除了显示hello world以外,我们在这里还看到了刚刚说到的第三方控件:SurveyPrompt,果然不简单嘛,一个看似简单的页面,居然还告诉了我们如何使用自定义控件。
从声明上看,我们知道SunveyPrompt是一个控件,并且有一个属性Title。现在我们打开它的代码:
<div class="alert alert-survey" role="alert"><span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span><strong>@Title</strong>Please take our<a target="_blank" class="alert-link" href="https://go.microsoft.com/fwlink/?linkid=870381">brief survey</a>and tell us what you think. </div>@functions {// This is to demonstrate how a parent component can supply parameterspublic string Title { get; set; } }
我们可以看到代码分为两部分,@functions上面是类似html的东西,下面是类似C#的东西。熟悉React或者Vue的伙伴们恐怕不会对这种混写感到陌生。这个就是Blazor的语法,Html部分很像使Razor的模板方式,而最后整个页面都会被编译成一个类,这个类派生自 Component。如果你编译过项目,你会在Debug下面的Shared目录找到一个叫SurveyPrompt.g.cs的东西:
#pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Shared/SurveyPrompt.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "a2a2ea88635b799343bc6d9647bbb818c8a20c9d" // <auto-generated/> #pragma warning disable 1591 namespace BlazorDemo.Shared {#line hiddenusing System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Net.Http;using Microsoft.AspNetCore.Blazor;using Microsoft.AspNetCore.Blazor.Components;using Microsoft.AspNetCore.Blazor.Layouts;using Microsoft.AspNetCore.Blazor.Routing;using BlazorDemo;using BlazorDemo.Shared;public class SurveyPrompt : Microsoft.AspNetCore.Blazor.Components.BlazorComponent{#pragma warning disable 1998protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder){base.BuildRenderTree(builder);builder.OpenElement(0, "div");builder.AddAttribute(1, "class", "alert alert-survey");builder.AddAttribute(2, "role", "alert");builder.AddContent(3, "\n ");builder.OpenElement(4, "span");builder.AddAttribute(5, "class", "glyphicon glyphicon-ok-circle");builder.AddAttribute(6, "aria-hidden", "true");builder.CloseElement();builder.AddContent(7, "\n ");builder.OpenElement(8, "strong");builder.AddContent(9, Title);builder.CloseElement();builder.AddContent(10, "\n\n Please take our\n ");builder.OpenElement(11, "a");builder.AddAttribute(12, "target", "_blank");builder.AddAttribute(13, "class", "alert-link");builder.AddAttribute(14, "href", "https://go.microsoft.com/fwlink/?linkid=870381");builder.AddContent(15, "\n brief survey\n ");builder.CloseElement();builder.AddContent(16, "\n and tell us what you think.\n");builder.CloseElement();builder.AddContent(17, "\n\n");}#pragma warning restore 1998// This is to demonstrate how a parent component can supply parameterspublic string Title { get; set; }} } #pragma warning restore 1591
我们发现@functions里面的内容会作为这个类的成员变量和成员方法,而上面的内容则被编译到了BuildRenderTree方法中。
那么到了这里我们大概知道了这个简单的HomePage都有什么玄机了。我们也大概知道了Blazor的语法,也知道其实我们所有的页面最终都会是一个Componet。
那么什么是Componet呢? 在这里并不想用过多的笔墨介绍这个概念。如果你是一个Vue或者React的开发,你应该对这个模块化开发不陌生。一个Componet,就是满足一定的功能,有自己的属性、状态,可以展示特定数据的元素。
就如同我们这里的SurveyPrompt,接受一个Title属性,并且负责把它展示成这样子:
四、Blazor的刷新和绑定机制初探
现在我们知道了一个简单的页面是如何渲染出来的,那么让我们打开Counter这个配置来看一看数据是如何交互的。
我们第二个page是这样子:
有一个button,当我们点击的时候,上面的current count 变成了 1:
这一切是怎么发生的呢? 以下是Counter.cshtml的代码:
@page "/counter" <h1>Counter</h1><p>Current count: @currentCount</p><button @onclick(IncrementCount)>Click me</button>@functions {int currentCount = 0;void IncrementCount(){currentCount++;} }
我们看到这个页面非常简单,我们定义了一个CurrentCount的Field,然后在IncreaseCount方法里给它加一,一个叫Click me的button标签里有一个@onclick方法,将IncreaseCount作为参数。
Counter.cshtml编译后的代码是这样的:
#pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Pages/Counter.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "05ad2dd449cbc9f09f8b759e1f06e7eb5e9583b4" // <auto-generated/> #pragma warning disable 1591 namespace BlazorDemo.Pages {#line hiddenusing System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Net.Http;using Microsoft.AspNetCore.Blazor;using Microsoft.AspNetCore.Blazor.Components;using Microsoft.AspNetCore.Blazor.Layouts;using Microsoft.AspNetCore.Blazor.Routing;using BlazorDemo;using BlazorDemo.Shared;[Microsoft.AspNetCore.Blazor.Layouts.LayoutAttribute(typeof(MainLayout))][Microsoft.AspNetCore.Blazor.Components.RouteAttribute("/counter")]public class Counter : Microsoft.AspNetCore.Blazor.Components.BlazorComponent{#pragma warning disable 1998protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder){base.BuildRenderTree(builder);builder.OpenElement(0, "h1");builder.AddContent(1, "Counter");builder.CloseElement();builder.AddContent(2, "\n\n");builder.OpenElement(3, "p");builder.AddContent(4, "Current count: ");builder.AddContent(5, currentCount);builder.CloseElement();builder.AddContent(6, "\n\n");builder.OpenElement(7, "button");builder.AddAttribute(8, onclick(IncrementCount));builder.AddContent(9, "Click me");builder.CloseElement();builder.AddContent(10, "\n\n");}#pragma warning restore 1998int currentCount = 0;void IncrementCount(){currentCount++;}} } #pragma warning restore 1591
我们看到@onclick其实在这里就是执行了一个Component的一个方法onclick,顾名思义,当这个Component被点击的时候就被调用。我们的IncreaseCount被作为参数传给了它,可见onclick会在被点击的时候执行IncreaseCount。
那么问题来了,当我们执行了IncreaseCount方法时,页面怎么会知道要不要刷新? 是刷新整个页面还是刷新所有?
熟悉WPF的同学可能知道,在WPF中如果我们需要让一个ViewModel可以被监听变化,它就需要实现INotifyChanged事件。那么同样道理,我们的这个IncreaseCount可能也是类似的吗?
然而基于编译后的代码我们可以发现 CurrentCount作为我们Counter这个类的Field,并没有任何机会高速Page自己变化了。而且这个Field非常普通,也不是什么WPF中的DP(依赖属性),所以到目前为止变化是怎么通知的。并没有一个合理的解释。后面的时间里我会尝试阅读Blazor的代码搞清楚这件事情。
第一个问题画个问号,那么第二个问题呢?
打开浏览器工具,定位到button,再次点击button观察dom的反应。
我们看到 在点击Button的时候,button上面的<p>标签闪动了,说明它被刷新了,而其他标签并没有。所以局部刷新的功能是有的,效率问题不用担心了。
编辑Click me,把他的内容变成 "点击我",再次点击按钮,我们看到还是只有p变,而且button也没有变回原来的内容:
所以我们知道,这个局部刷新不是简单的拿Dom作比较,肯定是有Virtual Dom的机制在里面。
五、星星之火,可燎原?
在简单的尝试了Blazor之后,还是很兴奋的。可以看到Blazor是一个初具规模的产品,我们C#开发可以用Blazor在今后写前端渲染的网页了!
我很期望这样一个产品能够持续的演进下去。
就目前版本看(0.1.0)Blazor尚不能应用到产品中。主要还是有以下的原因:
- 打包大小太大,1.8M的大小对于网站简直是致命的;
- 产品还不成熟,现在Component还只能支持简单的事件,笔者测试的时候只有onclick,onchange;
- 兼容性差,使用了WebAssembly,就注定了两年前的浏览器必定不能支持;
当然我们还是不能否认,Blazor为如何让更多语言进入前端世界打开了一扇新的大门。也许未来JavaScript将不仅仅是前端唯一可以使用的利器。我们会看到C/C++、Python、Java写的前端渲染页面也不一定呢。
当然在后端语言打入前端世界的道路上,WebAssembly也未必是唯一的方法,比如Scala.js就完全使用了js重写了Scala的库函数,类似的还有Kotlin.js。可以看到虽然JavaScript已经非常Fancy了,但是后端程序员们进军前端的热情可谓从未停歇过啊。
转载链接:https://www.cnblogs.com/Gerryz/p/get-start-with-dotnet-blazor.html
转载于:https://www.cnblogs.com/supersnowyao/p/8645530.html
开包即食的教程带你浅尝最新开源的C# Web引擎Blazor相关推荐
- 来自后端的突袭? --开包即食的教程带你浅尝最新开源的C# Web引擎 Blazor
在今年年初, 恰逢新春佳节临近的时候. 微软给全球的C#开发者们, 着实的送上了一分惊喜. 微软正式开源Blazor ,将.NET带回到浏览器. 这个小惊喜, 迅速的在dotnet开发者中间传开了. ...
- 非酋用计算机,拯救非酋带你入欧 炉石传说玄学开包经验
原标题:拯救非酋带你入欧 炉石传说玄学开包经验 众所周知炉石传说是一款暴雪推出的有魔兽背景的卡牌游戏,既然叫卡牌游戏那要想在游戏中战无不胜,就必须得有好卡.如何才能获得好卡,当然是开卡包了.但是这个开 ...
- canvas js 绘图插件_[开盖即食]小程序图表插件eCharts实战
H5时代用来做图表的插件有很多比如:ECharts.Bizcharts.JSCharts等,而这次的小程序本人选用了 ECharts 作为图表组件. 1.选择原因主要有3点: 官方某度在持续维护这个插 ...
- 长虹官方刷机包和刷机教程
为了解决部分朋友因应用引起的电视死机.无法开机.系统被破坏等情形,长虹电视团队特开此帖为朋友们提供刷机方法,但刷机有风险,如完全不懂刷机技巧的朋友需要谨慎操作哦,如有疑问可以微信留言给我们. 下面是按 ...
- 救世之树服务端开服架设服务器搭建教程
救世之树服务端开服架设服务器搭建教程 救世之树架设教程 准备好服务端(版本).服务器.域名开始实操:我是艾西 需要给服务器开启虚拟内存,设置好后服务器需要重启下 第一步:解压服务端到D盘 右键[000 ...
- mdx格式的词典用什么软件打开_欧路词典包资源及使用教程
欧路词典包资源及使用教程 学英语的或者经常用到英语的人都知道,市面上最通用的某道和某山等查词软件的语料库不够完善,甚至释义有时候都是不太准确的,以网络释义居多,经常查一个词这些软件会给出很多资料.解释 ...
- 保姆级教程带你在VMware中安装Linux Debinan操作系统
保姆级教程带你在VMware中安装Linux Debinan操作系统 文章目录 保姆级教程带你在VMware中安装Linux Debinan操作系统 1.Debinan系统介绍 2.Debinan操作 ...
- 我的世界html导入整合包教程,我的世界Windows整合包移植到Mac教程
我的世界Windows整合包移植到Mac教程,游戏狗小编下面为大家带来如何将我的世界Windows整合包移植到Mac的方法介绍,感兴趣的一起来看看我的世界Windows整合包移植到Mac教程. 1.启 ...
- Python:函数的参数、混搭、开包
一.位置参数 def calc(a, b, c):print(a*a + 2*b +c) calc(3, 5, 8) #27 位置参数适合参数少的情况,当参数较多时,位置容易存错. 二.关键词参数 d ...
最新文章
- linux amd显卡下载,下载:AMD显卡Linux催化剂驱动9.10版
- 造出世界最大芯片的公司,Cerebras Systems到底是怎样的存在?
- Xcode的Architectures、Valid Architectures和Build Active Architecture Only属性
- JAVA操作MYSQL数据库
- Mybatis【#{}和${}的区别】
- HDU - 4705 Y(树形dp)
- Springboot Freemarker 将模板放在jar包外面
- markdown语法简明笔记
- oracle 清除参数,IMp回去的时候要把原来的表的记录清空吗?没有什么参数可以省略这个吗...
- 某些函数原型及其用法
- paip.python错误解决2
- 用计算机给plc编程,plc编程用什么电脑?plc编程用什么配置电脑
- HDU 1069 Monkey and Banana(二维偏序LIS的应用)
- ANIMO 03 汉化补丁
- SQL题库:sqllearning4​—触发器、数据库存储练习
- Mendix低代码平台,唯快不破
- 闪存芯片NAND FLASH的封装
- 网络经济与企业管理【一】之企业管理概论
- win7无法进入计算机 如何修复工具,笔者教你win7进不了系统怎么办
- 【Javascript】Js闭包
热门文章
- Twelfth3: Keepalived highly available Ceph deployed distributed storage | Cloud computing
- 【WINDOWS / DOS 批处理】findstr命令之参数详解(二)
- sustech solidowrks
- 关于uniapp多图片上传和单图片上传
- PageHelper失效问题
- netty——黏包半包的解决方案、滑动窗口的概念
- 海岸鸿蒙正己烷中石油类质控,正己烷中石油类(紫外法)
- 常见http错误代码及含义!!
- 方差分析与一元线性回归
- MSIL简介 - Part 3 - 定义类型