在今年年初,恰逢新春佳节临近的时候。微软给全球的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;

使用命令行初始化项目:

  1. dotnet new -i Microsoft.AspNetCore.Blazor.Templates
  2. dotnet new blazor -o BlazorApp1
  3. cd BlazorApp1
  4. 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..的页面,这个页面的代码是这样的:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>BlazorDemo</title>
  6. <base href="/" />
  7. <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
  8. <link href="css/site.css" rel="stylesheet" />
  9. </head>
  10. <body>
  11. <app>Loading...</app>
  12. <script src="css/bootstrap/bootstrap-native.min.js"></script>
  13. <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>
  14. </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:

  1. @using System.Net.Http
  2. @using Microsoft.AspNetCore.Blazor
  3. @using Microsoft.AspNetCore.Blazor.Components
  4. @using Microsoft.AspNetCore.Blazor.Layouts
  5. @using Microsoft.AspNetCore.Blazor.Routing
  6. @using BlazorDemo
  7. @using BlazorDemo.Shared

Program.cs是程序的入口点:

  1. using Microsoft.AspNetCore.Blazor.Browser.Rendering;
  2. using Microsoft.AspNetCore.Blazor.Browser.Services;
  3. using System;
  4.  
  5. namespace BlazorDemo
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. var serviceProvider = new BrowserServiceProvider(configure =>
  12. {
  13. // Add any custom services here
  14. });
  15.  
  16. new BrowserRenderer(serviceProvider).AddComponent<App>("app");
  17. }
  18. }
  19. }

在入口点中,我们注册了一个浏览器渲染服务 BrowserRender,让他渲染App。

App.cshmtl是这样的:

  1. <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的代码是这个样子的:

  1. @page "/"
  2.  
  3. <h1>Hello, world!</h1>
  4. Welcome to your new app.
  5. <SurveyPrompt Title="How is Blazor working for you?" />

@page 可以告诉Router,当前页面注册到 "/"。

除了显示hello world以外,我们在这里还看到了刚刚说到的第三方控件:SurveyPrompt,果然不简单嘛,一个看似简单的页面,居然还告诉了我们如何使用自定义控件。

从声明上看,我们知道SunveyPrompt是一个控件,并且有一个属性Title。现在我们打开它的代码:

  1. <div class="alert alert-survey" role="alert">
  2. <span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
  3. <strong>@Title</strong>
  4.  
  5. Please take our
  6. <a target="_blank" class="alert-link" href="https://go.microsoft.com/fwlink/?linkid=870381">
  7. brief survey
  8. </a>
  9. and tell us what you think.
  10. </div>
  11.  
  12. @functions
  13. {
  14. // This is to demonstrate how a parent component can supply parameters
  15. public string Title { get; set; }
  16. }

我们可以看到代码分为两部分,@functions上面是类似html的东西,下面是类似C#的东西。熟悉React或者Vue的伙伴们恐怕不会对这种混写感到陌生。这个就是Blazor的语法,Html部分很像使Razor的模板方式,而最后整个页面都会被编译成一个类,这个类派生自 Component。如果你编译过项目,你会在Debug下面的Shared目录找到一个叫SurveyPrompt.g.cs的东西:

  1. #pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Shared/SurveyPrompt.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "a2a2ea88635b799343bc6d9647bbb818c8a20c9d"
  2. // <auto-generated/>
  3. #pragma warning disable 1591
  4. namespace BlazorDemo.Shared
  5. {
  6. #line hidden
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Threading.Tasks;
  11. using System.Net.Http;
  12. using Microsoft.AspNetCore.Blazor;
  13. using Microsoft.AspNetCore.Blazor.Components;
  14. using Microsoft.AspNetCore.Blazor.Layouts;
  15. using Microsoft.AspNetCore.Blazor.Routing;
  16. using BlazorDemo;
  17. using BlazorDemo.Shared;
  18. public class SurveyPrompt : Microsoft.AspNetCore.Blazor.Components.BlazorComponent
  19. {
  20. #pragma warning disable 1998
  21. protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
  22. {
  23. base.BuildRenderTree(builder);
  24. builder.OpenElement(, "div");
  25. builder.AddAttribute(, "class", "alert alert-survey");
  26. builder.AddAttribute(, "role", "alert");
  27. builder.AddContent(, "\n ");
  28. builder.OpenElement(, "span");
  29. builder.AddAttribute(, "class", "glyphicon glyphicon-ok-circle");
  30. builder.AddAttribute(, "aria-hidden", "true");
  31. builder.CloseElement();
  32. builder.AddContent(, "\n ");
  33. builder.OpenElement(, "strong");
  34. builder.AddContent(, Title);
  35. builder.CloseElement();
  36. builder.AddContent(, "\n\n Please take our\n ");
  37. builder.OpenElement(, "a");
  38. builder.AddAttribute(, "target", "_blank");
  39. builder.AddAttribute(, "class", "alert-link");
  40. builder.AddAttribute(, "href", "https://go.microsoft.com/fwlink/?linkid=870381");
  41. builder.AddContent(, "\n brief survey\n ");
  42. builder.CloseElement();
  43. builder.AddContent(, "\n and tell us what you think.\n");
  44. builder.CloseElement();
  45. builder.AddContent(, "\n\n");
  46. }
  47. #pragma warning restore 1998
  48.  
  49. // This is to demonstrate how a parent component can supply parameters
  50. public string Title { get; set; }
  51. }
  52. }
  53. #pragma warning restore 1591

我们发现@functions里面的内容会作为这个类的成员变量和成员方法,而上面的内容则被编译到了BuildRenderTree方法中。

那么到了这里我们大概知道了这个简单的HomePage都有什么玄机了。我们也大概知道了Blazor的语法,也知道其实我们所有的页面最终都会是一个Componet。

那么什么是Componet呢? 在这里并不想用过多的笔墨介绍这个概念。如果你是一个Vue或者React的开发,你应该对这个模块化开发不陌生。一个Componet,就是满足一定的功能,有自己的属性、状态,可以展示特定数据的元素。

就如同我们这里的SurveyPrompt,接受一个Title属性,并且负责把它展示成这样子:

四、Blazor的刷新和绑定机制初探

现在我们知道了一个简单的页面是如何渲染出来的,那么让我们打开Counter这个配置来看一看数据是如何交互的。

我们第二个page是这样子:

有一个button,当我们点击的时候,上面的current count 变成了 1:

这一切是怎么发生的呢? 以下是Counter.cshtml的代码:

  1. @page "/counter"
  2. <h1>Counter</h1>
  3.  
  4. <p>Current count: @currentCount</p>
  5.  
  6. <button @onclick(IncrementCount)>Click me</button>
  7.  
  8. @functions {
  9. int currentCount = 0;
  10.  
  11. void IncrementCount()
  12. {
  13. currentCount++;
  14. }
  15. }

我们看到这个页面非常简单,我们定义了一个CurrentCount的Field,然后在IncreaseCount方法里给它加一,一个叫Click me的button标签里有一个@onclick方法,将IncreaseCount作为参数。

Counter.cshtml编译后的代码是这样的:

  1. #pragma checksum "/Users/pzhi/SCM/gitHub/zhipu123/BlazorDemo/Pages/Counter.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "05ad2dd449cbc9f09f8b759e1f06e7eb5e9583b4"
  2. // <auto-generated/>
  3. #pragma warning disable 1591
  4. namespace BlazorDemo.Pages
  5. {
  6. #line hidden
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Threading.Tasks;
  11. using System.Net.Http;
  12. using Microsoft.AspNetCore.Blazor;
  13. using Microsoft.AspNetCore.Blazor.Components;
  14. using Microsoft.AspNetCore.Blazor.Layouts;
  15. using Microsoft.AspNetCore.Blazor.Routing;
  16. using BlazorDemo;
  17. using BlazorDemo.Shared;
  18. [Microsoft.AspNetCore.Blazor.Layouts.LayoutAttribute(typeof(MainLayout))]
  19. [Microsoft.AspNetCore.Blazor.Components.RouteAttribute("/counter")]
  20. public class Counter : Microsoft.AspNetCore.Blazor.Components.BlazorComponent
  21. {
  22. #pragma warning disable 1998
  23. protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
  24. {
  25. base.BuildRenderTree(builder);
  26. builder.OpenElement(, "h1");
  27. builder.AddContent(, "Counter");
  28. builder.CloseElement();
  29. builder.AddContent(, "\n\n");
  30. builder.OpenElement(, "p");
  31. builder.AddContent(, "Current count: ");
  32. builder.AddContent(, currentCount);
  33. builder.CloseElement();
  34. builder.AddContent(, "\n\n");
  35. builder.OpenElement(, "button");
  36. builder.AddAttribute(, onclick(IncrementCount));
  37. builder.AddContent(, "Click me");
  38. builder.CloseElement();
  39. builder.AddContent(, "\n\n");
  40. }
  41. #pragma warning restore 1998
  42.  
  43. int currentCount = ;
  44.  
  45. void IncrementCount()
  46. {
  47. currentCount++;
  48. }
  49. }
  50. }
  51. #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

开包即食的教程带你浅尝最新开源的C# Web引擎Blazor的更多相关文章

  1. 来自后端的突袭? --开包即食的教程带你浅尝最新开源的C# Web引擎 Blazor

    在今年年初, 恰逢新春佳节临近的时候. 微软给全球的C#开发者们, 着实的送上了一分惊喜. 微软正式开源Blazor ,将.NET带回到浏览器. 这个小惊喜, 迅速的在dotnet开发者中间传开了. ...

  2. 开包即食的教程带你浅尝最新开源的C# Web引擎 Blazor

    在今年年初, 恰逢新春佳节临近的时候. 微软给全球的C#开发者们, 着实的送上了一分惊喜. 微软正式开源Blazor ,将.NET带回到浏览器. 这个小惊喜, 迅速的在dotnet开发者中间传开了. ...

  3. [微信开发利器]微信内移动前端开发抓包调试工具fiddler使用教程

    [微信开发利器]微信内移动前端开发抓包调试工具fiddler使用教程   在朋友圈看到一款疯转的H5小游戏,想要copy,什么?只能在微信里打开?小样,图样图森破,限制了oauth.微信浏览器内打开, ...

  4. fiddler2抓包工具使用图文教程

    fiddler2抓包工具使用图文教程 三.fiddler实用功能使用说明: 1.fiddler捕获浏览器的会话: 能支持http代理的任意程序都能被fiddler捕获到,由于fiddler的运行机制就 ...

  5. 完美:adobe premiere cs6破解版下载[序列号+汉化包+破解补丁+破解教程]

    原文地址:http://blog.sina.com.cn/s/blog_6306f2c60102f5ub.html 完美:adobe premiere cs6破解版下载,含序列号.汉化包.注册机.破解 ...

  6. GIS空间分析案例教程——带背景和周围要素的逐要素导出地理

    GIS空间分析案例教程--带背景和周围要素的逐要素导出地理 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 目的:导出多边形要素类的每个要素 实 ...

  7. 最新最最最简单的Snagit傻瓜式破解教程(带下载地址)

    最新最最最简单的Snagit傻瓜式破解教程(带下载地址) 下载地址 直接滑至文章底部下载 软件介绍 一个非常著名的优秀屏幕.文本和视频捕获.编辑与转换软件.可以捕获Windows屏幕.DOS屏幕:RM ...

  8. 教程:在 Visual Studio 中开始使用 Flask Web 框架

    教程:在 Visual Studio 中开始使用 Flask Web 框架 Flask 是一种轻量级 Web 应用程序 Python 框架,为 URL 路由和页面呈现提供基础知识. Flask 被称为 ...

  9. 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

    原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

随机推荐

  1. 【文文殿下】浅析scanf源码

    本文仅做理性上的愉悦,无实际用途. scanf实际的调用 我们直接使用的scanf其实是这样写的 int __cdecl scanf ( const char *format, ... ) { va_ ...

  2. jQuery基础笔记(5)

    day56 参考:https://www.cnblogs.com/liwenzhou/p/8178806.html#autoid-1-9-5 文档处理 添加到指定元素内部的后面 $(A).append ...

  3. 日志分析与splunk浅谈

    难易程度:★★★ 阅读点:linux;python;web安全;日志分析; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 linux下的日志分析对企业来说非常重要,对我们分析p ...

  4. JSON.stringify和JSON.parse的使用

    JSON.stringify 函数 (JavaScript)将 JavaScript 值转换为 JavaScript 对象表示法 (Json) 字符串.JSON.stringify(value [, ...

  5. linux,软链接配置node,npm全局命令

    sudo ln -s /usr/local/bin/node   /bin/node sudo ln -s /usr/local/bin/npm    /bin/npm 这样配置后,在root下和别的 ...

  6. .gitignore中添加的某个忽略文件并不生效

    最近项目中,来了一新同事,协同开发的过程中,发现老是提示pod install,于是照做了,做完项目可以跑成功但发现提示我跟同事一样的问题,Podfile.lock文件需要提交,于是便提交了,然而同事 ...

  7. 安卓7.0拍照遇到 Uri暴露错误

    最近,项目又做到,调用摄像头拍照获取图片这个功能. 用以前的代码直接用,发现在Android7.0上使用时会出现问题. Android6.0之后,动态申请权限已成常态. 调用摄像头拍照获取图片这个功能 ...

  8. Android版本分布——2017年5月更新

    Code Name Version API Level Last month This month Change gingerbread(姜饼) 2.3.3——2.3.7 10 0.9% 1.0% 0 ...

  9. Spring Boot 的彩色日志

    springboot的彩色日志灰常漂亮, 看起来也很舒服. 但是自定义的日志就是一纯白色的, 丑到不行. 所以就copy他的彩色日志来养眼: <!-- 彩色日志 --> <!-- 彩 ...

  10. zabbix邮件内容乱码与邮件内容为附件解决办法

    在zabbix的实际使用过程中,在收到邮件预警的时候,我们会发现邮件内容是乱码的,在手机端收到的是附件,而且附件下载后的文件类型是打不开的.这样我们不知道我们是哪个服务器的哪项服务出了问题,接下来我们 ...