Blazor简介和工程搭建

Blazor是微软为ASP.NET Core开发的前端框架,它能让开发者使用C#语言构建基于浏览器的交互式Web前端应用程序。使用C#开发前端的体验是非常特别的,这和传统的JavaScript/TypeScript前端技术栈有相似点但也有很大区别。本系列笔记我们将对Blazor的概念和使用方法进行学习,所有内容将基于最新的.NET8版本进行介绍。

注:学习该章节需要掌握ASP.NET Core WebAPI/MVC/SignalR章节中介绍的所有内容,ASP.NET Core中一些通用的部分包括依赖注入、配置框架、Razor模板语法等不会在这里重复介绍了,此外最好还要掌握现代前端开发相关知识,如果你不熟悉前端开发直接学习Blazor,一些概念可能难以理解。

Blazor的渲染模式

Blazor有3种渲染模式,这和目前前端领域最火热的框架之一NextJS有些类似(但也并不完全等同),Blazor的3种渲染模式我们可以类比NextJS中的SSG、SSR和CSR渲染模式。

静态渲染模式:类似NextJS的SSG模式,Web内容完全由服务端静态生成,用户在页面上的操作不会直接和服务端C#代码发生交互。静态渲染模式的使用场景和Hexo之类的静态生成器比较类似,可用于那些不需要动态交互的内容呈现。与静态渲染模式相对的是交互式渲染模式,下面两种都属于交互式渲染。

BlazorServer模式:类似NextJS的SSR模式,Web内容是由服务端动态计算生成的,不过BlazorServer的组件状态是维护在服务端的,这又和JavaEE中的JSF有些类似,用户打开页面后会同时自动建立SignalR连接,组件状态的变化将自动通过SignalR反映到页面上,用户对页面组件的操作也会通过SignalR将页面组件状态保存在BlazorServer的服务端内存中。由于前端组件状态保存在远程服务器上,这种渲染模式带来了两个缺点,一是前后端交互频繁,在网速较低的情况下有明显延迟和卡顿感,体验较差,二是一旦网站访问量较高服务端内存开销将比较大。

WebAssembly模式:类似NextJS的CSR模式,该模式将创建纯前端工程,它可以完全脱离服务端在浏览器中运行。不过浏览器毕竟不能直接执行C#代码,因此这种模式会将工程以WebAssembly方式编译。WebAssembly模式的Blazor工程会在页面首次打开时加载一系列WASM运行时文件,因此首次打开页面时有一个较慢加载的过程,这也是该模式的一个缺点。

在.NET8中,我们其实还可以混合使用这些渲染模式实现更好的用户体验,这部分内容将在后续章节介绍。

Blazor的优缺点

了解了Blazor的3种渲染模式,其实我们也就大致知道了Blazor的优缺点。优点自然是能够使用强大的C#语言同时编写前端和后端工程,可以让开发者抛开大部分恼人的JavaScript代码,技术栈统一、开发效率高;Blazor框架设计的也是十分简单易用,而前端生态的React、Angular等则相对要“抽象”很多;当然,Blazor的缺点也很明显,前面我们介绍的BlazorServer模式和WebAssembly模式都有各自的“硬伤”,做不到JavaScript系列的现代前端框架那么完美。

此外,Blazor的生态也相对奇葩和小众,如果你要实现的功能比较简单还好(例如那些只有CRUD的2B项目),一旦要实现的功能较为复杂,可能还是绕不开JavaScript的API,这又额外需要Blazor和JavaScript进行互操作,引入了额外的麻烦,这也是使用Blazor不得不面对的困境。

浏览器兼容性

根据微软官方文档,.NET8下的Blazor兼容最新版本的Google Chrome、Mozilla Firefox、Microsoft Edge和Apple Safari,不支持Internet Explorer。如果你还想保持网站的IE兼容性,则无法使用最新版本的Blazor。

在Visual Studio中创建Blazor工程

在.NET8中,创建Blazor工程默认只有一个项目模板Blazor Web App

在创建的过程中有一个选项Interactive render mode,选择None表示静态渲染,Server表示BlazorServer模式渲染,WebAssembly表示WebAssembly模式渲染,Auto则是BlazorServer模式和WebAssembly模式混合渲染。我们可以逐一尝试,看看默认生成的代码和浏览器中的表现具体有什么区别。

本系列笔记将先主要介绍BlazorServer模式的开发,WebAssembly模式和混合渲染模式将在后续章节补充介绍。

BlazorServer工程目录结构

Visual Studio默认创建的Blazor工程目录结构如下。

|_ bin                             # 编译输出目录,不需要手动编辑
|_ obj                             # 编译的中间文件目录,不需要手动编辑
|_ Properties                      # 用于存放与项目运行和配置相关的文件
  |_ launchSettings.json           # 配置开发环境下的运行和调试行为,不会被发布到生产环境中
|_ Components                      # 前端页面和组件文件夹
  |_ Layout                        # 页面基础框架文件夹,其中的组件在Routes.razor中被引入
  |_ Pages                         # 页面文件夹,内部的组件被路由系统根据浏览器中的URL加载
  |_ _Imports.razor                # 名字固定,统一导入文件,包含应用程序中常用的命名空间和组件的导入
  |_ App.razor                     # Blazor根组件,用于定义应用程序的路由和布局
  |_ Routes.razor                  # 单独定义的路由组件,通常用于集中存放和管理路由配置
|_ wwwroot                         # 静态资源文件夹
|_ Program.cs                      # 工程入口文件
|_ appsettings.json                # 工程配置文件
|_ appsettings.Development.json    # 分环境的工程配置文件
|_ DemoBlazorServer.csproj         # 工程描述文件

可以看到,BlazorServer工程也是一种ASP.NET Core工程,它们有相似的结构,只是Program.cs中配置了不同的服务和中间件。Components文件夹是Blazor独有的,其中使用定义了许多.razor结尾的Blazor组件。

最后我们观察一下Program.cs文件。

using Gacfox.DemoBlazorServer.Components;

var builder = WebApplication.CreateBuilder(args);

// 注入RazorComponents服务和交互式组件服务,允许BlazorServer应用使用交互式组件
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
}

// 启用静态文件服务
app.UseStaticFiles();
// 启用防CSRF攻击功能
app.UseAntiforgery();

// 映射Razor组件路由,设置根组件为App,并启用交互式服务器渲染模式
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

// 启动并运行应用程序
app.Run();

可以看到Program.cs中我们注册了BlazorServer相关的服务,最后使用MapRazorComponents()方法加载了Blazor根组件并启动交互式渲染模式,然后启动了应用程序。

Hello, world!

我们这里编写一个简单的例子体验Blazor的使用,下面例子创建了一个页面,它是一个反复在红蓝之间切换的Hello, Blazor!文本。

Components/Pages/Hello.razor

@page "/hello"
@rendermode InteractiveServer

<h3 style="@colorStyle">Hello, Blazor!</h3>

@code {
    private string colorStyle = "color: red";
    private Timer? _timer;

    protected override void OnInitialized()
    {
        _timer = new Timer(ToggleColor, null, 0, 1000);
    }

    private void ToggleColor(object? state)
    {
        colorStyle = colorStyle == "color: red" ? "color: blue" : "color: red";
        InvokeAsync(StateHasChanged);
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

Blazor组件通常由三部分构成。第一部分是指令,@page指定了页面路由,@rendermode设置为InteractiveServer指定该页面是一个交互模式的BlazorServer组件。第二部分是Razor模板,它在HTML内嵌入的Razor语法。第三部分是一个@code {}块,其中包含了C#代码,colorStyle是一个组件的变量,它绑定到了页面元素的style属性上,用于控制颜色;_timer是一个定时器,OnInitialized()会在组件初始化时回调,代码中我们初始化了定时器并让其每隔1秒调用ToggleColor()切换colorStyle的值,这间接实现了页面元素颜色的修改;Dispose()会在组件销毁时调用,我们在这里销毁了定时器。ToggleColor()函数中还调用了InvokeAsync(StateHasChanged)StateHasChanged()函数用于通知Blazor组件重新渲染,InvokeAsync()是确保在异步操作中正确调度UI更新的必要方法,这能够避免线程安全问题。

BlazorSerever组件会在服务端运行,colorStyle变量在服务端内存里被不断修改,这一修改会通过SignalR将状态告知浏览器,浏览器就会最终渲染出页面元素颜色不断跳动的效果。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap