创建项目
安装 .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--;
}
}
复制代码
路由
大概可以猜测到,上面的 @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
组件中有 Found
和 NotFound
两部分,其中 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);
}
}
复制代码
可以看到,我们为 input
的 onchange
事件绑定了 SetStep
,用于更新设置的步长,并将步长传入 StepCounter
的参数 Step
中。
总结
Blazor 以完全相同的模板语法和语言提供了两种原理上大相径庭的方式:WebAssembly 项目的逻辑都在客户端运行,而 Server 端项目则是将用户的操作通过 SignalR 框架,借助 WebSocket 实时发送到后端。
具体的,在 Server 端项目中,通过 Chrome DevTool 可以看到,每次点击都会发送一个 DispatchBrowserEvent 的事件,并携带用户点击的位置,按键情况等信息:
并且,将 dotnet watch run
启动的进程关闭后,Blazor 客户端应用会提示错误。
这种方式有以下特点:
- 首页面加载速度快,需要带宽少,不需要加载庞大的 js 或 wasm 库,相当于服务端渲染
- 服务端可以方便的收集用户操作日志
- 用户点击响应在高时延下响应较慢
- 一些即使纯前端的需求,也必须后端服务器才能满足(例如计数器)
WebAssembly 则通过 JavaScript 加载 WebAssembly 模块,用 WebAssembly 替代 JavaScript 完成前端逻辑计算的功能,这种方式有以下特点:
- 首页加载速度很慢,一般比 JavaScript 库更慢,因为还需要加载 WebAssembly 模块的时间
- 一些耗费性能的运算相较 JavaScript 将更快,例如矩阵运算等
- DOM 更慢,因为目前 WebAssembly 操作 DOM 还需要借助 JavaScript 间接进行
总而言之,Blazor 为前端项目的设计与实现提供了全新且较为完整的方式,且语法和实现方式总体较为直观并与 React 等典型前端框架类似,对性能有较高要求或以 C# 为技术栈的项目将会有很大帮助。