先上一张图,看一下高大上的WebAssembly 宣传图片是什么样子
非常美观,作为一个已经快被淘汰的.net程序员,今天抽空看一下新技术,有一天万一失业了,出去了也能胡吹冒料一把。好了,废话不多说,先看一下什么是Blazor。
Blazor是用於使用.NET構建交互式客戶端Web UI的框架:
使用C#而不是JavaScript創建豐富的交互式UI 。
共享用.NET編寫的服務器端和客戶端應用程序邏輯。
將UI渲染為HTML和CSS,以提供廣泛的瀏覽器支持,包括移動瀏覽器。
與現代託管平台(例如Docker)集成。
使用.NET進行客戶端Web開發具有以下優點:
用C#而不是JavaScript編寫代碼。
利用現有的.NET庫的.NET生態系統。
在服務器和客戶端之間共享應用程序邏輯。
受益於.NET的性能,可靠性和安全性。
使用Windows,Linux和macOS上的Visual Studio保持高效。
建立在一組穩定,功能豐富且易於使用的通用語言,框架和工具上。
WebAssembly 正在 W3C Web 组装社区组中创建为开放标准,具有以下目标:
快速、高效和可移植 – WebAssembly 代码可以通过利用常见的硬件功能,在不同平台上以近乎原生的速度执行。
可读和可调试 – WebAssembly 是一种低级组合语言,但它具有人工可读的文本格式(规范仍在最终确定中),允许手动编写、查看和调试代码。
保持安全 – WebAssembly 被指定在安全的沙盒执行环境中运行。与其他 Web 代码一样,它将强制实施浏览器的相同源和权限策略。
不要破坏 Web – WebAssembly 的设计是为了与其他 Web 技术很好地发挥,并保持向后兼容性。
OK,这些概念性的东西网上搜一搜看一看就好,我们看一下入门Demo,其实当我创建好这项目之后我发信其实它的代码结构感觉和VS自带的React 模板有点类似。首先我们打开VS2019,创建新项目。
ok,接着下一步,我们给项目起个名字,叫WebAssemblyDemo吧。
ok,接着下一步,我们给项目起个名字,叫WebAssemblyDemo吧。
点击创建我们的项目就创建好了,总共三个项目,一个是Client端,一个是Server端,另一个是Shared,这个Shared里面就是放的客户端和服务端公用的东西。首先我们来看一下客户端的代码,非常的熟悉的结构。
启动部分依然是program.cs,启动方法依然是main
public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); }
加载root Component,注入服务,启动。从这里可以看到入口一定是app这个组件,咋感觉是在说react一样,看一下App.razor的代码
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
在这里,你所看到的Router标签,命名空间是Microsoft.AspNetCore.Component.Routing,对于所有使用到的命名空间,都存在于_Imports.razor文件中,如下
@using System.Net.Http @using System.Net.Http.Json @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop @using WebAssemblyDemo.Client @using WebAssemblyDemo.Client.Shared
但是这些class可以直接当做标签写到这里,真是不可思议。上面这段指定了App需要加载的程序集。这里的Found顾名思义,意思是发现路由,这里的Context用来指定RouteView节点中的routeData参数名称,在RouteView节点中,RouteData是指上面自动加载程序集后所产生的的路由集合,对于每个路由,指定DefaultLayout类型为MainLayout。从这些代码可以看出,这段其实就是加载所有Assembly并形成路由集合,指定每个路由的默认布局。
OK,看一下MainLayout的代码
@inherits LayoutComponentBase <div class="sidebar"> <NavMenu /> </div> <div class="main"> <div class="top-row px-4"> <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a> </div> <div class="content px-4"> @Body </div> </div>
我们看到他继承了LayoutComponentBase,这个类就类似于我们之前学习的webforms,所有page都继承System.Web.UI.Page,拿React来说,又像是React.Component一样,总之他是所有Componet的基类。在这段代码中,@Body有点类似于MVC中的@RenderBody(),或者angular中的router-outlet一样,路由跳转请求的页面内容都会被替换到这个位置。另外我们发现有一个<NavMenu />标签,这个NavMenu 又是另一个Component,我们看一下它的代码。
<div class="top-row pl-4 navbar navbar-dark"> <a class="navbar-brand" href="">WebAssemblyDemo</a> <button class="navbar-toggler" @onclick="ToggleNavMenu"> <span class="navbar-toggler-icon"></span> </button> </div> <div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> <ul class="nav flex-column"> <li class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <span class="oi oi-home" aria-hidden="true"></span> Home </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="oi oi-plus" aria-hidden="true"></span> Counter </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="fetchdata"> <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data </NavLink> </li> </ul> </div> @code { private bool collapseNavMenu = true; private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; private void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } }
这段代码,我们可以看到button的点击事件,div的class绑定,以及三个路由和C#代码@code部分。路由部分很简单了,它的href属性路由到指定的组件。第一个home,href="",后面的Match设置的是All,意思是除了counter和fetachdata这两个组件的路由以外,其他的统统都路由到home页面。从code这里可以看出是实现了一个toggle功能,点击顶部的WebAssemblyDemo链接,就会展开或者折叠导航菜单。这里的coder部分的属性变化可以实时反映到UI,类似于angular和react,vue的双向绑定触发UI更新。
OK,我们看一下第一个路由到的页面home,它的代码如下
@page "/" <h1>Hello, world!</h1> Welcome to your new app. <SurveyPrompt Title="How is Blazor working for you?" />
还是熟悉的Hello word,最顶上一段用来指定该组件的路由地址,当我们点击home菜单的时候,就会路由到这个地址。同时这个页面它还内嵌了另一个组件,SurveyPrompt,看一下
<div class="alert alert-secondary mt-4" role="alert"> <span class="oi oi-pencil mr-2" aria-hidden="true"></span> <strong>@Title</strong> <span class="text-nowrap"> Please take our <a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2127996">brief survey</a> </span> and tell us what you think. </div> @code { // Demonstrates how a parent component can supply parameters [Parameter] public string Title { get; set; } }
这个页面上有个title,在code部分,这个Title属性被打了标签,意思是这个属性可以由父组件设置。所以在上面的父组件中,Title被设置为How is Blazor working for you?,OK我们看一下这个页面的运行效果
没有什么问题,再看一下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++; } }
同样的最顶端是路由地址,点button的时候,计数器会自动加1,这太特么像react的demo了。
最后我们再看一下FetchData.razor
@page "/fetchdata" @using WebAssemblyDemo.Shared @inject HttpClient Http <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } }
这个页面也太熟悉了,顶部声明一个注入的HttpClient实例Http,然后请求api http://localhost:44381/WeatherForecast,拿到数据后UI异步更新,感觉和前端那些MVVM没什么区别。OK,我们看一下运行的效果
到此,所有的页面都讲完了,可能还有个事没搞清楚,就是css这些文件在哪里。想想其实和react,angular那些VS自带的脚手架一样,一定是index.html。
就是这里的index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>WebAssemblyDemo</title> <base href="/" /> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> </head> <body> <app>Loading...</app> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">??</a> </div> <script src="_framework/blazor.webassembly.js"></script> </body> </html>
所有的组件加载上来都是在<app></app>这里进行替换展示。
最后再看一下VS2019 React脚手架项目结构,像神了。
上一篇 如何终止多个Task