Blazor 服务器和 WebAssembly 应用程序的初学者指南
如果您一直在了解 .NET 世界的最新发展趋势,那么您现在一定听说过 Blazor。目前在 .NET 社区中对 Blazor 进行了大量炒作,这种炒作的最常见原因是它引入了大多数 .NET 开发人员几十年来一直梦想的东西,即不仅可以在服务器上运行 C#但也在浏览器中。 Blazor 允许我们使用 HTML、CSS 和 C# 而不是 JavaScript 来构建交互式 Web 应用程序。在本教程中,我将介绍 Blazor 的基本概念,并将概述可用于 Blazor 的不同托管模型。我还将介绍每种托管模型的优缺点,以便您可以为下一个 Blazor 项目确定最佳托管模型。
什么是 Blazor?
Blazor 是一个免费的开源单页应用程序 (SPA) 开发框架,使开发人员能够在服务器和客户端上使用 C# 构建交互式 Web 应用程序。 Blazor 不需要在客户端上安装任何插件来在浏览器中执行 C#/.NET 代码。它使用 WebAssembly 执行 .NET 代码,WebAssembly 是所有主要浏览器都支持的 Web 标准。 Blazor 还可以运行 .NET 代码并在服务器上构建 UI,并通过 SignalR 连接仅将更新的 DOM 传输到客户端。
什么是 WebAssembly?
WebAssembly(有时缩写为 Wasm)是一种可移植的二进制格式(低级指令集),旨在在任何能够解释这些指令的主机上运行。 WebAssembly 的主要目标是允许开发人员构建高性能的 Web 应用程序,但该格式也旨在执行并集成到其他环境中。 WebAssembly 目前受到所有主要浏览器的支持,例如 Chrome、Android 版 Chrome、Edge、Firefox、Safari、Opera 等。
Blazor 托管模型
Blazor 组件模型是 Blazor 的核心,它的设计方式使计算 UI 更改和呈现 UI 彼此分开。这就是为什么无论您使用何种方法渲染应用程序,基本组件模型都不会改变。在撰写本文时,有四种渲染/托管模型可用,它们都处于不同的开发阶段。
- Blazor Server
- Blazor WebAssembly
- Blazor Electron
- Mobile Blazor Bindings
Blazor Electron 和 Mobile Blazor Bindings 目前处于试验阶段,Microsoft 尚未承诺提供这些托管模型,因此我不会在本文中讨论它们。
什么是 Blazor 服务器应用程序?
Blazor 服务器应用在服务器上运行,在那里它们享受完整 .NET Core 运行时的支持。所有处理都在服务器上完成,UI/DOM 更改通过 SignalR 连接传输回客户端。这种双向 SignalR 连接是在用户第一次在浏览器中加载应用程序时建立的。由于您的 .NET 代码已经在服务器上运行,因此您无需为前端创建 API。您可以直接访问服务、数据库等,在传统的服务器端技术上做任何想做的事情。
何时使用 Blazor 服务器
- 当您想要在完整的 .NET Core 运行时上运行您的应用程序时
- 当您希望将应用程序的初始下载大小保持在很小的时候
- 当你想保持你的应用程序启动时间非常快时
- 当您希望将应用程序的代码保留在服务器上并且不希望将其下载到客户端时。
- 当您希望应用程序的快速开发周期且现有 .NET 开发人员几乎没有学习曲线时
- 当您想让您的应用对搜索引擎友好时
- 当您希望您的应用程序在不依赖于 WebAssembly 的旧浏览器上运行时
- 当您想在 Visual Studio 中像任何普通 .NET应用程序一样调试 .NET 代码时
- 当您想要构建 Intranet 或低需求面向公众的应用程序时
何时不使用 Blazor 服务器
- 当您的应用程序在高延迟环境中运行时
- 当您希望您的应用在没有与服务器保持 SignalR 连接的情况下脱机工作时
- 当您不想增加服务器资源来处理大量连接的 SignalR 客户端时。
什么是 Blazor WebAssembly 应用程序?
这种托管模型是现代流行的 SPA 框架(如 Angular、Vue 和 React)的直接竞争对手,这是大多数开发人员有兴趣学习 Blazor 的主要原因。它允许开发人员使用 C# 而不是 JavaScript 编写所有前端 UI 逻辑。在此托管模型中,应用程序 DLL、任何依赖项和小尺寸 Mono .NET 运行时都会在第一个请求中下载到客户端。一旦客户端上的所有内容都可用,Mono 运行时就会加载并执行应用程序代码。 Blazor WebAssembly 程序可以用 C、C# 等其他语言编写,然后编译成 WebAssembly 字节码。
何时使用 Blazor WebAssembly
- 当您想要将整个应用程序编译为静态文件并将它们提供给客户端时,不需要服务器上的 .NET 运行时。这意味着您的后端可以用 PHP、Node或 Rails 编写,并且可以为用 Blazor 编写的前端应用程序提供服务。
- 当您想要构建可以在客户端脱机运行而无需持续连接到服务器的应用程序时
- 当您想将处理转移到客户端并想减少服务器上的负载时
- 当您想在客户端和服务器之间共享代码和库时
何时不使用 Blazor WebAssembly
- 当由于下载到客户端的文件/DLL 太多而无法在有效负载上妥协时
- 当您无法在缓慢的启动时间上妥协时,尤其是在互联网连接不佳的情况下
- 当您无法妥协这一事实时,该应用程序必须在具有所有安全限制的浏览器沙箱中运行。
在 Visual Studio 2019 中创建 Blazor 服务器应用程序
打开 Visual Studio 2019 社区版,然后单击创建新项目选项。从可用模板列表中选择 Blazor 应用程序模板,然后单击下一步。
提供项目名称,例如 BlazorServerApp,然后单击下一步。您将看到以下对话框,要求您选择要创建的 Blazor 应用程序类型。我们正在创建 Blazor 服务器应用程序,因此选择 Blazor 服务器应用程序并单击创建按钮。
Visual Studio 将使用解决方案资源管理器中显示的以下文件夹和文件为我们创建 Blazor 服务器应用程序。
让我们了解 Blazor 服务器应用程序中可用的一些重要文件和文件夹。
Program.cs
该文件包含作为项目入口点的 Main 方法。 main 方法调用 CreateHostBuilder 方法,该方法为我们的应用程序配置默认 ASP.NET Core 主机。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
这与我们在标准 ASP.NET Core 项目中使用的文件相同。需要注意的重要一点是 ConfigureServices 方法正在调用 AddServerSideBlazor 方法。此方法添加与 Blazor 服务器应用程序相关的服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
我们在 Configure 方法中还有以下两行重要的代码。 MapBlazorHub 方法配置 Blazor 服务器应用程序所需的 SignalR 中心终结点。 MapFallbackToPage 方法将所有这些请求映射到 _Host 页面,这些页面没有映射到任何控制器、razor页面等。这将允许所有动态内容请求路由到 SPA 框架而不是抛出 404 Not Found。
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
_Host.cshtml
这是应用程序的根页面,每个 Razor 组件/页面都将在此主机页面中呈现。它具有基本的 HTML 元素,例如 html、head 和 body,以及一些特殊元素。请注意,Blazor 是一个基于组件的框架,Blazor 中的一切都是一个组件。指定了我们想要呈现应用程序根组件的位置。
<component type="typeof(App)" render-mode="ServerPrerendered" />
此文件还在末尾注入 blazor.server.js 文件,此 JavaScript 文件包含设置 SignalR 连接到服务器的代码。此连接在应用程序加载到浏览器中后立即建立,然后用于服务器和客户端浏览器之间的实时通信。
<script src="_framework/blazor.server.js"></script>
App.razor
这是 Blazor App 的主要组件,其主要工作是拦截路由并呈现 Found 或 NotFound 组件。如果找到与路由匹配的组件,则呈现 Found 组件,如果未找到匹配的组件,则呈现 NotFound 组件。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<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>
MainLayout.cshtml
MainLayout 文件包含应用程序的主布局,其标记可以与多个 Razor 组件共享。这个布局组件通常包含应用程序的常见 UI 元素,例如页眉、菜单、页脚、侧边栏等。为我们生成的默认 MainLayout 有一个侧边栏来呈现 NavMenu 组件,它也使用 Razor 语法 @Body 来指定布局标记中其他组件的内容将呈现的位置。
@inherits LayoutComponentBase
<div class="page">
<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>
</div>
wwwroot 文件夹
该文件夹包含静态文件,例如图像、字体、图标、CSS 和 JavaScript 文件等。
Pages and Shared 文件夹
该文件夹包含我们之前讨论的 _Host.cshtml 文件以及一些 Razor 组件。 Blazor 应用程序是具有 .razor 扩展名的 Razor 组件的集合。其中一些组件被称为可路由组件,因为它们可以使用它们的路由进行访问。例如,当我们导航到应用程序根 URL 时,将呈现以下 Index.razor 组件。 URL 是使用 Index.razor 组件顶部的 @page 指令指定的。
Index.razor
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
注意,上面的页面还使用了一个子组件 SurveyPrompt,它被称为子组件,因为它没有@page 指令,它可以嵌入到其他组件中。
Pages 文件夹中还有一些其他的 razor 组件,这些组件都可以使用在文件顶部指定的路径进行访问。例如,当我们导航到 /counter 路径时,Counter 组件将呈现。同样, FetchData 组件将使用 /fetchdata 路径呈现。
Razor Server 应用程序还有一个包含共享组件的共享文件夹。这些组件可以被整个应用程序中的任何组件使用,就像我们上面看到的 SurveyPrompt 组件一样。 Shared 文件夹中另一个有趣的共享组件是 NavMenu 组件,它呈现我们的 Blazor 服务器应用程序的顶部导航栏。
_Imports.razor
该文件类似于我们在 ASP.NET MVC Web 应用程序中的 _ViewImports.cshtml 文件,它包含我们可以在不同 razor 组件中使用的命名空间列表。在 _Imports.razor 文件中声明所有这些命名空间的好处是我们不需要在每个 razor 组件中重复导入它们。
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
现在是时候运行我们的 Blazor 服务器应用程序并在浏览器中查看它的运行情况。在 Visual Studio 中按 F5,您将看到一个漂亮的默认 Blazor 服务器应用程序。尝试从侧边栏导航到不同的页面,并尝试在 Counter 页面上使用计数器,您会注意到没有页面刷新或回发到服务器。一切都感觉流畅和快速,就像典型的 SPA 一样,所有浏览器和服务器的通信都是使用 SignalR 连接进行的。
您还可以打开浏览器开发人员工具,您会注意到包括 blazor.server.js 文件在内的所有标准 CSS 和 JavaScript 文件都已下载到客户端,并通过 Web 套接字建立了 SignalR 连接。
在 Visual Studio 2019 中创建 Blazor WebAssembly 应用程序
我们已经了解了 Blazor 服务器应用程序的基础知识,并在浏览器中看到了它的运行情况。现在让我们创建一个 Blazor WebAssembly 应用程序,以便我们可以看到不同之处。按照我们上面提到的相同步骤,并使用 Blazor 应用程序模板在 Visual Studio 中创建一个新的 Blazor 应用程序。当你被要求选择 Blazor App 的类型时,你这次需要选择 Blazor WebAssembly App。
Visual Studio 将使用解决方案资源管理器中显示的以下文件夹和文件为我们创建 Blazor WebAssembly 应用程序。
您可以轻松发现这两种类型的应用程序之间的一些差异。例如,我们在 Blazor WebAssembly 应用程序中没有以下文件。
- _Host.cshtml
- Error.cshtml
- Startup.cs
- appsettings.json
index.html
在 Blazor WebAssembly 应用程序中,我们在 wwwroot 文件夹中有一个 index.html 文件,用作根页面。此文件在最后注入 blazor.webassembly.js 文件,该文件由框架提供以处理下载 .NET 运行时、我们的 Blazor 应用程序和所有应用程序依赖项。它还具有初始化运行时以运行应用程序的代码。
Program.cs
在 Blazor WebAssembly 应用程序中,应用程序的根组件在 Program.cs 文件中可用的 Main 方法中指定。应用程序的根组件是 App.razor,你可以看到它是如何添加到 RootComponents 集合中的。
public class Program
{
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();
}
}
在 Visual Studio 中按 F5,您将看到一个外观相似的 Blazor WebAssembly 应用程序。尝试从侧边栏导航到不同的页面,并尝试像之前在 Blazor Server App 中所做的那样在 Counter 页面上使用计数器。一切看起来和感觉都一样,没有服务器端回传。
正如我们已经知道的那样,Blazor WebAssembly 应用程序会在客户端下载应用程序及其所有依赖项,因此如果您打开浏览器开发人员工具,您可以看到客户端上下载了大量 DLL。
以上所有文件只会在第一个请求中下载,然后它们将被缓存在浏览器中。如果您再次刷新页面,您只会看到这次下载的文件较少。
总结概括
在这篇文章中,试图为您提供 Blazor SPA 框架的基本概述,我们已经看到两个 Blazor 应用程序使用两种不同的托管模型进行托管。两个项目中的大部分代码和文件都相同,因为 Blazor 框架严重依赖于 razor 组件。这些组件是 Blazor 应用程序的构建块,无论我们使用什么托管模型,我们都可以以类似的方式构建这些组件。如果您喜欢这篇文章,请分享它并传播知识。
![](https://images.cnblogs.com/cnblogs_com/cool2feel/1535460/o_IMG_20190827_161853.jpg)
您的资助是我最大的动力!
金额随意,欢迎来赏!