2021年01月26日 23:09
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

先上一张图,看一下高大上的WebAssembly 宣传图片是什么样子

v2-0d6e86d503b37a6fbdd23b6d5931d206_720w.png

非常美观,作为一个已经快被淘汰的.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保持高效

  • 建立在一組穩定,功能豐富且易於使用的通用語言,框架和工具上。

Blazor WebAssembly在具有WebAssembly的瀏覽器中運行.NET代碼。


WebAssembly 正在 W3C Web 组装社区组中创建为开放标准,具有以下目标:

快速、高效和可移植 – WebAssembly 代码可以通过利用常见的硬件功能,在不同平台上以近乎原生的速度执行。

可读和可调试 – WebAssembly 是一种低级组合语言,但它具有人工可读的文本格式(规范仍在最终确定中),允许手动编写、查看和调试代码。

保持安全 – WebAssembly 被指定在安全的沙盒执行环境中运行。与其他 Web 代码一样,它将强制实施浏览器的相同源和权限策略。

不要破坏 Web – WebAssembly 的设计是为了与其他 Web 技术很好地发挥,并保持向后兼容性。

 

OK,这些概念性的东西网上搜一搜看一看就好,我们看一下入门Demo,其实当我创建好这项目之后我发信其实它的代码结构感觉和VS自带的React 模板有点类似。首先我们打开VS2019,创建新项目。

 ok,接着下一步,我们给项目起个名字,叫WebAssemblyDemo吧。

image.png

ok,接着下一步,我们给项目起个名字,叫WebAssemblyDemo吧。

image.png

点击创建我们的项目就创建好了,总共三个项目,一个是Client端,一个是Server端,另一个是Shared,这个Shared里面就是放的客户端和服务端公用的东西。首先我们来看一下客户端的代码,非常的熟悉的结构。

image.png

启动部分依然是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我们看一下这个页面的运行效果

image.png

没有什么问题,再看一下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了。

image.png

最后我们再看一下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,我们看一下运行的效果

image.png到此,所有的页面都讲完了,可能还有个事没搞清楚,就是css这些文件在哪里。想想其实和react,angular那些VS自带的脚手架一样,一定是index.html。

image.png

就是这里的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脚手架项目结构,像神了。

image.png

发表评论
匿名  
用户评论
暂无评论