Blazor 入门:利用 WebAssembly 或服务端渲染,用 C# 编写 Web 前端应用

创建项目

安装 .NET SDK

这个网站可以下载到最新的 .NET SDK,值得一提的是,.NET 分为以下三种:

  • .NET Framework 4.8 一个较为老旧,仅能在 Windows 运行且过去闭源的运行时,已经过时
  • .NET Core 开源,且支持 Linux, MacOS 等全平台的运行时,目前 LTS 版本为 3.0
  • .NET 5.0 其实就是下一代的 .NET Core。为了避免和 .NET Framework 4.x 系列混淆,跳过了 4.x 版本。而 .NET Core 是 .NET 后续唯一的主要版本,故 5.0 中省略 Core

创建 WebAssembly 项目

Blazor 可以利用 WebAssembly 直接在浏览器中运行 C# 代码。且因是通过 WebAssembly 运行的完整的 .NET 框架,我们可以在应用中重用服务器端的代码和库。

具体的,用下面的脚手架初始化一个项目:

dotnet new blazorwasm -o BlazorPlaygroundWasm --no-https
复制代码

将项目运行起来

dotnet run
复制代码

看到如下的输出后,即可访问 http://localhost:5000

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Code\BlazorPlaygroundWasm
复制代码

创建 Server 端项目

另外,Blazor 还可以将网页端逻辑置于服务器端运行,客户端的 UI 事件通过一个实时消息框架 SignalR 发回服务器。当服务器执行完毕时,需要变更的 UI 被发回客户端,并被合入 DOM 中。

创建一个 Server 项目:

dotnet new blazorserver -o <项目名> --no-https
复制代码

具体的,对于不同的项目项目大致结构如下:

  • Program.cs 为 App 的入口,并启动服务器
  • Startup.cs 用于配置 App 服务和中间件,仅在 Server 端项目存在
  • App.razor 是 App 的根组件
  • BlazorApp/Pages 目录包括了一些 App 的示例页面
  • BlazorApp.csproj 定义了项目以及它的依赖,WebAssembly 项目和 Server 端项目即主要在此有所不同。

Blazor 基础使用

数据绑定与事件绑定

让我们以 Counter.razor 为一个例子,看看 Blazor 中的逻辑:

@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 的语法将变量 currentCount 的值绑定到 DOM 节点 p
  • 事件绑定:通过 @onclick="IncrementCount 将按钮的 click 事件绑定为触发 IncrementCount

让我们尝试修改以下上面的代码,将点击自增改为点击递减:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="DecrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void DecrementCount()
    {
        currentCount--;
    }
}
复制代码

image.png

路由

大概可以猜测到,上面的 @page "/counter" 表示当路径为 /counter 时,渲染该页面,我们可以修改后进行验证。

而在 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 组件中有 FoundNotFound 两部分,其中 NotFound 显然为未找到路由时的提示信息,而 Found 则是找到路由时显示的信息,可以看到当 NotFound 时,展示的是 Sorry, there's nothing at this address. 这行提示信息,而当 Found 时,则是展示 RouteView,即应展示的路由内容。

MainLayout 则表示了除主体外的布局,具体的,在文件 Shared/MainLayout.razor 下定义:

@inherits LayoutComponentBase

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <div class="content px-4">
        @Body
    </div>
</div>
复制代码

其中,引用了 NavMenu,定义了侧边栏的样式。

组件参数

首先,我们将 Counter 内代码移动到 Shared/StepCounter.razor 中,并做一些修改:

<h1>StepCounter</h1>

<p>Step:</p>

<p>Current count: @currentCount</p>


<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    [Parameter]
    public int Step { get; set; } = 1;

    private void IncrementCount()
    {
        currentCount += this.Step;
    }
}
复制代码

具体的,上面的语法中,其中 [Parameter] 为 C# 中的 Attribute 语法,类似 JavaScript 的 Decorator,标识下面的属性式组件的一个参数:

[Parameter]
public int Step { get; set; } = 1;
复制代码

而原来的 Counter 中,我们按照如下方法修改:

@page "/counter"

<h1>Counter</h1>


<p>Step:</p>
<input @onchange="SetStep" />


<StepCounter Step="@Step"></StepCounter>

@code {
    public int Step { get; set; } = 1;


    private void SetStep(ChangeEventArgs e) {
        Console.WriteLine(e.Value);
        Step = int.Parse((string)e.Value);
    }
}
复制代码

可以看到,我们为 inputonchange 事件绑定了 SetStep,用于更新设置的步长,并将步长传入 StepCounter 的参数 Step 中。

image.png

总结

Blazor 以完全相同的模板语法和语言提供了两种原理上大相径庭的方式:WebAssembly 项目的逻辑都在客户端运行,而 Server 端项目则是将用户的操作通过 SignalR 框架,借助 WebSocket 实时发送到后端。

具体的,在 Server 端项目中,通过 Chrome DevTool 可以看到,每次点击都会发送一个 DispatchBrowserEvent 的事件,并携带用户点击的位置,按键情况等信息:

image.png

并且,将 dotnet watch run 启动的进程关闭后,Blazor 客户端应用会提示错误。
这种方式有以下特点:

  • 首页面加载速度快,需要带宽少,不需要加载庞大的 js 或 wasm 库,相当于服务端渲染
  • 服务端可以方便的收集用户操作日志
  • 用户点击响应在高时延下响应较慢
  • 一些即使纯前端的需求,也必须后端服务器才能满足(例如计数器)

WebAssembly 则通过 JavaScript 加载 WebAssembly 模块,用 WebAssembly 替代 JavaScript 完成前端逻辑计算的功能,这种方式有以下特点:

  • 首页加载速度很慢,一般比 JavaScript 库更慢,因为还需要加载 WebAssembly 模块的时间
  • 一些耗费性能的运算相较 JavaScript 将更快,例如矩阵运算等
  • DOM 更慢,因为目前 WebAssembly 操作 DOM 还需要借助 JavaScript 间接进行

总而言之,Blazor 为前端项目的设计与实现提供了全新且较为完整的方式,且语法和实现方式总体较为直观并与 React 等典型前端框架类似,对性能有较高要求或以 C# 为技术栈的项目将会有很大帮助。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享