Blazor
是一个使用.NET生成交互式客户端WebUI的框架:
使用C#代替JavaScript来创建信息丰富的交互式UI。
共享使用.NET编写的服务器端和客户端应用逻辑。
将UI呈现为HTML和CSS,以支持众多浏览器,其中包括移动浏览器。
与新式托管平台(如Docker)集成。
使用.NET进行客户端Web开发可提供以下优势:
使用C#代替JavaScript来编写代码。
利用现有的.NET库生态系统。
在服务器和客户端之间共享应用逻辑。
受益于.NET的性能、可靠性和安全性。
在Windows、Linux和macOS上使用VisualStudio保持高效工作。
以一组稳定、功能丰富且易用的通用语言、框架和工具为基础来进行生成。
Blazor
应用基于组件。Blazor
中的组件是指UI元素,例如页面、对话框或数据输入窗体。
组件是内置到.NET
程序集的.NET
C#
类,它们用于:
定义灵活的UI呈现逻辑。
处理用户事件。
可以嵌套和重用。
可作为Razor类库或NuGet包共享和分发。
组件类通常以Razor
标记页(文件扩展名为.razor
)的形式编写。Blazor
中的组件有时被称为Razor
组件。Razor
是一种语法,用于将HTML标记与专为提高开发人员工作效率而设计的C#代码结合在一起。借助Razor
,可使用Visual Studio
中的IntelliSense
编程支持在同一文件中的HTML标记与C#之间切换。Razor Pages
和MVC也使用Razor
。与基于请求/响应模型生成的Razor Pages
和MVC
不同,组件专门用于处理客户端UI逻辑和构成。
Blazor使用UI构成的自然HTML标记。下面的Razor标记演示了一个组件(Dialog.razor
),它显示一个对话框,并处理在用户选择按钮时发生的事件:
@Title
@ChildContent
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Title { get; set; }
private void OnYes()
{
Console.WriteLine("Write to the console in C#! 'Yes' button selected.");
}
}
在上述示例中,OnYes
是由按钮的onclick
事件触发的C#方法。对话框的文本(ChildContent
)和标题(Title
)由在其UI中使用此组件的下述组件提供。
使用HTML标记将Dialog
组件嵌入到另一个组件中。在以下示例中,Index组件(Pages/Index.razor
)使用前面的Dialog
组件。标记的Title
属性向Dialog
组件的Title
属性传递标题的值。Dialog
组件的文本(ChildContent
)由元素的内容设置。向
Index
组件添加Dialog
组件后,Visual Studio
中的IntelliSense
可加快使用语法和参数补全进行开发的速度。
@page "/"
Hello, world!
Welcome to your new app.
在浏览器中访问父级Index
组件时,将呈现该对话框。当用户选择此按钮时,浏览器的开发人员工具控制台会显示由OnYes
方法编写的消息:
组件呈现为浏览器文档对象模型(DOM)
的内存中表现形式,它被称为“呈现树”,用于以灵活高效的方式更新UI。
文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。
一个web页面是一个文档。这个文档可以在浏览器窗口或作为HTML源码显示出来。但上述两个情况中都是同一份文档。文档对象模型(DOM)提供了对同一份文档的另一种表现,存储和操作的方式。DOM是web页面的完全的面向对象表述,它能够使用如 JavaScript等脚本语言进行修改。
Blazor Web Assembly
是单页应用(SPA)框架
,用于使用.NET
生成交互式客户端Web应用。Blazor Web Assembly
使用无插件或将代码重新编译为其他语言的开放式Web标准。Blazor Web Assembly
适用于所有新式Web浏览器,包括移动浏览器。
通过WebAssembly
(缩写为wasm
),可在Web浏览器内运行.NET
代码。Web Assembly
是针对快速下载和最大执行速度优化的压缩字节码格式。Web Assembly
是开放的Web标准,支持用于无插件的Web浏览器。
Web Assembly
代码可通过JavaScript
(称为JavaScript
互操作性,通常简称为JavaScript
互操作或JS互操作)访问浏览器的完整功能。通过浏览器中的Web Assembly
执行的.NET
代码在浏览器的JavaScript
沙盒中运行,沙盒提供的保护可防御客户端计算机上的恶意操作。
当Blazor Web Assembly
应用生成并在浏览器中运行时:
C#代码文件和Razor文件将被编译为.NET程序集。
该程序集和.NET运行时将被下载到浏览器。
BlazorWebAssembly启动.NET运行时,并配置运行时,以为应用加载程序集。BlazorWebAssembly运行时使用JavaScript互操作来处理DOM操作和浏览器API调用。
已发布应用的大小(其有效负载大小)是应用可用性的关键性能因素。大型应用需要相对较长的时间才能下载到浏览器,这会损害用户体验。Blazor Web Assembly
优化有效负载大小,以缩短下载时间:
在中间语言(IL)裁边器发布应用时,会从应用删除未使用的代码。
压缩HTTP响应。
.NET运行时和程序集缓存在浏览器中。
主要的Blazor
托管模型在Web Assembly
上的浏览器中运行客户端。将Blazor
应用、其依赖项以及.NET
运行时下载到浏览器。应用将在浏览器线程中直接执行。UI更新和事件处理在同一进程中进行。应用资产作为静态文件部署到可为客户端提供静态内容的Web服务器或服务中。
如果创建了Blazor Web Assembly
应用进行部署,但没有后端ASP.NET Core
应用来为其文件提供服务,那么该应用被称为独立Blazor Web Assembly
应用。如果创建了应用进行部署,但没有后端应用来为其文件提供服务,那么该应用被称为托管的Blazor Web Assembly
应用。托管的Blazor Web Assembly Client
应用通常使用WebAPI
调用或SignalR
(结合使用ASP.NET Core SignalR
和Blazor
)通过网络与后端Server应用交互。
blazor.webassembly.js
脚本由框架和句柄提供:
下载.NET运行时、应用和应用依赖项。
初始化运行应用的运行时。
Blazor Web Assembly
托管模型具有以下优点:
没有.NET服务器端依赖项。应用下载到客户端后即可正常运行。
可充分利用客户端资源和功能。
工作可从服务器转移到客户端。
无需ASP.NETCoreWeb服务器即可托管应用。无服务器部署方案可行,例如通过内容分发网络(CDN)为应用提供服务的方案。
Blazor Web Assembly
托管模型具有以下局限性:
应用仅可使用浏览器功能。
需要可用的客户端硬件和软件(例如WebAssembly支持)。
下载项大小较大,应用加载耗时较长。
.NET运行时和工具支持不够完善。例如,.NETStandard支持和调试方面存在限制。
Atwood 定律:任何能够用JavaScript编写的应用程序,最终必将用JavaScript编写。- Jeff Atwood
目前可通过两种通用方法来构建Web应用程序:在服务器上执行大部分应用程序逻辑的传统Web应用程序,以及在Web浏览器中执行大部分用户界面逻辑的单页应用程序(SPA)
,后者主要使用WebAPI
与Web服务器通信。也可以将两种方法混合使用,最简单的方法是在更大型的传统Web应用程序中托管一个或多个丰富SPA类子应用程序。
何时使用传统Web应用程序:
应用程序的客户端要求简单,甚至要求只读。
应用程序需在不支持JavaScript
的浏览器中工作。
团队不熟悉JavaScript
或TypeScript
开发技术。
何时使用SPA:
应用程序必须公开具有许多功能的丰富用户界面。
团队熟悉JavaScript
、TypeScript
或Blazor Web Assembly
开发。
应用程序已为其他(内部或公共)客户端公开API。
此外,SPA框架还需要更强的体系结构和安全专业知识。相较于传统Web应用程序,SPA框架需要进行频繁的更新和使用新框架,因此改动更大。相较于传统Web应用,SPA应用程序在配置自动化生成和部署过程以及利用部署选项(如容器)方面的难度更大。
使用SPA方法改进用户体验时必须权衡这些注意事项。
传统Web应用程序、SPA或Blazor应用之间进行选择时要考虑的一些基本因素
因素 | 传统 Web 应用 | 单页面应用程序 | Blazor 应用 |
---|---|---|---|
需要团队熟悉 JavaScript/TypeScript | 最低 | 必需 | 最低 |
支持不带脚本的浏览器 | 支持 | 不支持 | 支持 |
客户端应用程序行为极少 | 适合 | 不必要 | 可行 |
丰富而复杂的用户界面要求 | 受限 | 适合 | 适合 |
应用程序的客户端要求简单,可能要求只读
对许多Web应用程序而言,其大部分用户的主要使用方式是只读。只读(或以读取为主)应用程序往往比那些维护和操作大量状态的应用程序简单得多。例如,搜索引擎可能由一个带有文本框的入口点和用于显示搜索结果的第二页组成。匿名用户可以轻松提出请求,并且很少需要使用客户端逻辑。同样,一般而言,博客或内容管理系统中面向公众的应用程序主要包含的内容与客户端行为关系不大。此类应用程序容易构建为基于服务器的传统Web应用程序,在Web服务器上执行逻辑,并呈现要在浏览器中显示的HTML。事实上,网站的每个独特页面都有自己的URL,搜索引擎可以将其存为书签和编入索引(默认设置,无需将此功能添加为应用程序的单独功能),这也是此类情况的一个明显优势。
应用程序需在不支持JavaScript的浏览器中工作
如需在有限或不支持JavaScript
的浏览器中工作的Web应用程序,则应使用传统的Web应用工作流编写(或至少可以回退到此类行为)。SPA需要客户端JavaScript
才能正常工作;如果没有客户端JavaScrip
t,SPA不是好的选择。
团队不熟悉JavaScript或TypeScript开发技术
如果团队不熟悉JavaScript
或TypeScript
,但熟悉服务器端Web应用程序开发,那相较于SPA,他们交付传统Web应用的速度可能更快。除非以学习SPA编程为目的,或需要SPA提供用户体验,否则对已经熟悉构建传统Web应用的团队而言,选择传统Web应用的工作效率更高。
应用程序必须公开具有许多功能的丰富用户界面
SPA可支持丰富客户端功能,当用户执行操作或在应用的各区域间导航时无需重新加载页面。SPA很少需要重新加载整个页面,因此加载速度更快,可在后台提取数据,并且对单个用户操作的响应更快。SPA支持增量更新,可保存尚未完成的窗体或文档,而无需用户单击按钮提交窗体。SPA支持丰富的客户端行为,例如拖放,比传统应用程序更容易操作。可以将SPA设计为在断开连接的模式下运行,对客户端模型进行更新,并在重新建立连接后将更新最终同步回服务器。如果应用要求包括丰富的功能,且超出了典型HTML窗体提供的功能,则选择SPA样式应用程序。
通常,SPA需要实现内置于传统Web应用中的功能,例如在反映当前操作的地址栏中显示有意义的URL(并允许用户将此URL存为书签或对其进行深层链接以便返回此URL)。SPA还应允许用户使用浏览器的后退和前进按钮寻找用户意料之中的结果。
团队熟悉JavaScript和/或TypeScript开发
编写SPA需要熟悉JavaScript
或TypeScript
以及客户端编程技术和库。团队应有能力像使用Angular
一样使用SPA框架编写新式JavaScript
。
参考-SPA框架
Angular:https://angular.io
React:https://reactjs.org/
Vue.js:https://vuejs.org/
应用程序已为其他(内部或公共)客户端公开API
如果已提供一个Web API
供其他客户端使用,则相较于在服务器端窗体中复制逻辑,创建一个利用这些API
的SPA
实现更加容易。用户与应用程序交互时,SPA
广泛使用Web API
来查询和更新数据。
应用程序必须公开丰富用户界面
与基于JavaScript
的SPA一样,Blazor
应用程序可以支持丰富的客户端行为,而无需重载页面。这些应用程序对用户的响应更快,仅获取响应给定用户交互所需的数据(或HTML)。如果设计得当,可以将服务器端Blazor
应用配置为以客户端Blazor
应用的形式运行,只需在此功能受支持后对它进行稍加更改即可。
与使用JavaScript或TypeScript开发相比,团队更喜欢使用.NET开发
与使用JavaScript
或TypeScript
等客户端语言相比,许多使用.NET和Razor
的开发人员的工作效率更高。由于已经使用.NET开发了应用程序的服务器端,因此,使用Blazor
可以确保团队中的每名.NET开发人员都可以理解,并可能会生成应用程序前端的行为。
Blazor
将组件呈现逻辑从UI更新的应用方式中分离出来。Blazor Server
在ASP.NET Core
应用中支持在服务器上托管Razor
组件。可通过SignalR
连接处理UI更新。
运行时停留在服务器上并处理:
执行应用的C#代码。
将UI事件从浏览器发送到服务器。
将UI更新应用于服务器发送回的已呈现的组件。
Blazor Server
用于与浏览器通信的连接还用于处理JavaScript
互操作调用。
对于需要第三方JavaScript
库和访问浏览器API的应用,组件与JavaScript
进行互操作。组件能够使用JavaScript
能够使用的任何库或API。C#代码可调用到JavaScript
代码,而JavaScript
代码可调用到C#代码。
在ASP.NET Core Blazor中从.NET方法调用JavaScript函数
https://docs.microsoft.com/zh-cn/aspnet/core/blazor/call-javascript-from-dotnet?view=aspnetcore-5.0
从ASP.NET Core Blazor中的JavaScript函数调用.NET方法
https://docs.microsoft.com/zh-cn/aspnet/core/blazor/call-dotnet-from-javascript?view=aspnetcore-5.0
浏览者 | Version |
---|---|
Apple Safari,包括 iOS | 当前† |
Google Chrome,包括 Android | 当前† |
Microsoft Edge | 当前† |
Mozilla Firefox | 当前† |
†最新指的是浏览器的最新版本。
Blazor WebAssembly项目模板:blazorwasm
Blazor Server项目模板:blazorserver
如果之前没有安装的先安装这个SDK:
dotnet-sdk-5.0.202-win-x64.exe
如果已经安装过了,检查下当前版本:
dotnet --version
1) 命令行方式创建
dotnet new blazorserver -o BlazorApp --no-https
这里通过DOTNET-CLI
执行新建项目的命令,使用的是blazorserver
这个项目模板,输出项目文件夹为BlazorApp
,给该项目设置为不需要HTTPS模式--no-https
。
如果要创建Blazor WebAssembly
项目,这里将blazorserver
改成blazorwasm
即可。
dotnet new blazorwasm -o BlazorApp --no-https
2) Visual Studio方式创建
打开Visual Studio 2019
最新版本,创建新项目,找到Blazor
相关的项目模板。
Blazor Server应用
Blazor WebAssembly应用
点击下一步
,输入BlazorApp
的项目名称,点击下一步
进行创建。
目标框架,可以选.NET Core 3.1(长期支持)
,也可以选择.NET 5.0(当前)
,去掉勾选复选框配置HTTPS
,点击创建
即可。
创建成功之后,会自动打开项目解决方案。
1) 命令行方式创建
如果通过命令行方式创建,运行应用只需要执行:
dotnet watch run
通过DOTNET-CLI
的Run
命令可以运行程序,这里添加watch
参数的好处就是,等下如果改动了文件,会热重载,这样调试起来就很方便。
运行起来之后,会看到浏览器弹出应用首页。
如果要退出运行,只需要执行如下命令即可:
Ctrl + C
2) Visual Studio方式创建
在Visual Studio
里面运行就很简单了,直接点运行BlazorApp
或者Ctrl + F5
即可。
默认会打开一个终端控制台界面,用来显示Console日志。
运行起来之后,会看到浏览器弹出应用首页。
Program.cs
启用这个应用服务的入口节点。
Startup.cs
配置应用服务和中间件的地方。
App.razor
应用组件的根组件,也就是组件的入口位置。
BlazorApp/Pages
包含一些示例页面,也是页面目录。
BlazorApp.csproj
描述应用项目及其依赖关系。
我们看到Pages/Index.razor
这个主页。
@page "/"
Hello, world!
Welcome to your new app.
这里已经有了SurveyPrompt
这样一个组件,同时它有一个入参Title
可以定制。
打开Pages/Counter.razor
,这是一个计数器的组件。
@page "/counter"
Counter
Current count: @currentCount
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
我们可以从代码看到,顶部@page "/counter"
实际上这个页面组件的目录,当我们切换到它时,路径也会变化。
每次点击Click me
按钮的时候,就会从onclick
事件去找到IncrementCount
这个函数,在这个函数中,我们把currentCount
做了加1
,这时候,Current count
的值会跟着刷新,可以看出只要绑定好了数据,它就会帮我们自动去更新界面显示了,还是很方便。
打开Pages/Index.razor
,我们在末尾追加Counter
即可。
@page "/"
Hello, world!
Welcome to your new app.
这时候,我们查看页面就已经可以看到它了。
接下来,我们打开Pages/Counter.razor
,我们给这个组件添加一个参数属性IncrementAmount
,默认值是1
, 这样我们可以从外部传递值进来修改它,并且我们让currentCount
不再是继续加1
了,而是改成加IncrementAmount
的值,这样我们就可以控制每次累加的幅度。
@page "/counter"
Counter
Current count: @currentCount
@code {
private int currentCount = 0;
[Parameter]
public int IncrementAmount { get; set; } = 1;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}
然后,我们回到Pages/Index.razor
,在Counter
组件上给他传递IncrementAmount
的值进来。
@page "/"
Hello, world!
Welcome to your new app.
这时候,界面会自动重载,这时候,我们每次点击Click me
,这个计时器组件就会加10
了,哈哈,是不是很简单。
如果之前没有安装的先安装这个SDK:
dotnet-sdk-5.0.202-win-x64.exe
如果已经安装过了,检查下当前版本:
dotnet --version
dotnet new blazorserver -o TodoList --no-https
通过DOTNET-CLI
脚本的New
命令,新建一个blazorserver
项目模板,输出目录命名为TodoList
,同时不需要HTTPS
的支持。
cd TodoList
先切换到TodoList
项目文件夹中。
dotnet new razorcomponent -n Todo -o Pages
然后,通过DOTNET-CLI
脚本的New
命令,输出目录为Pages
,新建名为Todo
的razorcomponent
组件。
其中-n
全称为--name
,用来指定创建的名称,-o
全称为--output
,用来指定在这个目录进行创建。
Razor
组件文件名要求首字母大写。打开Pages
文件夹,确认Todo
组件文件名以大写字母T开头。文件名应为Todo.razor
。
将在项目文件夹的Pages
目录中,新建得到一个Todo.razor
文件。
接下来,我们需要在Todo.razor
文件顶部添加针对该文件组件的路由位置。
@page "/todo"
位于Shared
文件夹的NavMenu.razor
组件是控制左侧导航的,我们需要将“Todo”的Blazor组件添加到左侧导航中。
在NavMenu.razor
组件中,找到ul
元素下的li
节点,参考前面的例子,新建属于Todo
组件的导航位置。
这里我们需要注意的几个关键点是,NavLink
的href
需要填写组件的路由位置,其中span
元素是菜单图标,紧跟着它的就是菜单名称,这里我们用ToDoList
来作为菜单名称。
保存之后,我们通过执行run
命令来运行,查看导航效果。
dotnet watch run
这里一个小插曲就是为了更好的写出TodoItem.cs
这个模型类,我遇到了两个不错的VSC插件,这里就顺带记录一下。
名为C# Extensions的VSC插件,可以帮助我们支持在VSC中右键快速新建模型类和接口,有点类似VS中那种创建,至少帮你完成了当前命名空间的添加的活,所以值得推荐。
名为C# XML Documentation Comments的VSC插件,平时我们在VS中写代码,习惯性的如果要添加函数或者属性的注释的话,上来就是///
然后等待自动完成得到一串添加注释的注释体,这个插件就是来干这个事情的,效果还不错。
这两个插件,搜索
C#
这个关键词就可以看到了,基本上排在前面。
有了它们,我们创建TodoItem
模型类就如虎添翼了。
在项目的Data
文件夹中,我们右键,选择New C# Class
,输入TodoItem
,然后回车即可。
然后我们就自动完成,得到了一个基础的空的TodoItem.cs
模型类,它的namespace
都是已经处理好了,我觉得比手写强吧。
接下里,我们往TodoItem.cs
模型类中添加两个属性(最好是走prop
命令来新建),一个是Title
来存储待办事项的标题,一个是IsDone
来标记是否已完成该事项。
接下来,为了将来更好的阅读这个模型,我们养成一个好习惯,给对应的模型和字段都添加好注释,这时候C# XML Documentation Comments
这个VSC插件就要派上用场了,通过在所需要注释代码的开头位置,输入///
然后回车即可。
namespace TodoList.Data
{
///
/// 待办事项
///
public class TodoItem
{
///
/// 事项名称
///
///
public string Title { get; set; }
///
/// 事项是否已完成
///
///
public bool IsDone { get; set; }
}
}
ASP.NET Core Blazor 简介
BlazorFluentUI
Blazor Webassembly Demo
DOM概述
在传统 Web 应用和单页应用 (SPA) 之间选择
WebAssembly (abbreviated Wasm)
从 ASP.NET Core Blazor 中的 JavaScript 函数调用 .NET 方法
在 ASP.NET Core Blazor 中从 .NET 方法调用 JavaScript 函数
ASP.NET Core Blazor 托管模型
用于 ASP.NET Core Blazor 的工具
Blazor Tutorial - Build your first Blazor app
生成 Blazor 待办事项列表应用
C# Extensions - VS MarketPlace
C# XML Documentation Comments - VS MarketPlace