Blazor 通常被称为单页应用程序 (SPA) 框架。当我第一次开始使用 Blazor 时,我对 SPA 的含义、组件如何为 SPA 架构做出贡献以及所有这些如何与交互性联系在一起感到困惑。
今天,我将解答大家可能关心的三个问题:
- 什么是 SPA?
- 了解所有新的“Blazor Web App”模板。
- 是什么使 Blazor 成为 SPA 应用程序?
什么是 SPA?
图 1 显示了传统的 Web 应用程序如何处理用户请求,它通常涉及用户为每个新页面向服务器发出请求。
例如,向服务器发送对“www.facebook.com/home.html”这样的主页的请求,服务器处理该请求然后返回主页。而当用户导航到个人资料页面时,将发出另一个对“www.facebook.com/profile.html”的请求,服务器现在将返回个人资料页面。
每次交互都会导致整个页面重新加载。
图 1:传统 Web 应用架构
现在让我们看看 SPA 应用程序是如何工作的,
用户体验保持不变。然而,在服务器上,我们不再提供完整的网页,而是使用组件。这是因为 Blazor 是一个基于组件的单页应用程序。
您可能想知道,哪个单页?在图 2 所示的架构中,App.razor是所有用户请求定向到的网页。服务器加载此单页并将其替换为请求的组件。
例如,当用户请求主页时,App.razor 会加载Home.razor组件。同样,当用户请求个人资料页面时,服务器首先加载App.razor,然后再加载 Profile.razor。
如下所示,Profile组件有附加组件, UserInfo.razor和UserOrders.razor。这称为组件层次结构。
图 2:基于 Blazor SPA 组件的架构
相比之下,SPA 会加载单个页面,并在用户与应用交互时动态更新内容。这是通过动态加载组件来实现的,无需完全重新加载,从而带来更流畅的用户体验。
例如,在 YouTube 上,当你给视频添加评论时,评论部分会更新,而不会中断视频播放。这是因为评论部分是一个单独的组件,可以独立刷新,而不会影响其他部分。
创建 Blazor Web 应用
让我们通过创建 Blazor Web 应用程序来看一下它的实际效果。
- 打开 Visual Studio 并选择 Blazor Web App 模板。
图 3:创建新的 Blazor Web 应用程序
- 为您的应用命名并选择一个位置。
图 4:Blazor 中的项目配置
- 选择“.NET 8” 作为框架。将“身份验证类型”设置为“无” ,并为“交互式渲染模式”选择“无”,以确保我们使用 Blazor SSR(静态服务器端渲染)。忽略“交互位置选项”,因为当为交互式渲染模式选择“无” 时,它不适用。最后,点击创建按钮。
图 5:Blazor Web 应用的附加信息
在解决方案资源管理器中,您将看到类似以下的结构:
图 6:Blazor Web 应用文件夹结构
理解代码
首先,让我向您展示您请求的页面在 Blazor 应用中加载的顺序。请注意数字,因为它们代表控制流。我们将在下面详细介绍每个文件,解释它们在此过程中的作用。
图 7:Blazor Web 应用中的控制流
让我们回顾一下早期的 C# 时代。众所周知,Program.cs是 C# 应用程序的入口点,对于 Blazor 来说,它遵循同样的原则
以下是 Program.cs 中的代码
using MyFirstBlazorWebApp.Components;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>();
app.Run();
代码片段 1:Program.cs
该文件处理用户发出的 HTTP 请求(例如,/index、/profile),如上面的架构图所示。
从第 18行到第 21行,你可以看到专门用于管理这些 HTTP 请求的方法。在第 23行,HTTP 请求被映射到名为App 的组件。
你可能会问,这是什么应用程序?
这与我们架构图(图 2)中显示的App.razor相同,这意味着所有请求在到达App.razor之前都会流经Program.cs。
让我们探索一下 App.razor 中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="MyFirstBlazorWebApp.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
代码片段 2:App.razor
这主要是标准 HTML,但有一个明显的区别:在第 16行,它调用名为的组件。
组件的作用是定位并加载特定的请求组件。例如,当用户请求主页时,它会找到Home.razor,而当用户请求个人资料页面时,它会找到Profile.razor并替换它。
那么 组件包含什么?
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
代码片段 3:Routes.razor
Router组件负责定位请求的组件并将其应用到第 3 行的MainLayout。路由完成后,Router 组件会更新MainLayout。
让我们打开 MainLayout.razor
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
代码片段 4:MainLayout.razor
注意第 14行,其中有一个“@Body” 占位符。这是插入请求组件的位置。因此,当 请求“Home.razor”时,其 HTML 内容将替换“@Body” 占位符,整个页面将通过路由器发送回浏览器中显示。
提示:其余代码代表您的 Web 应用的布局。如果您想为您的应用定义一个带有侧边栏、页眉、页脚和动态中心内容的自定义布局,这里就是您要做的。
不要太担心这些理论;让我们继续运行该应用程序,看看它的实际作用。
图 8:加载 Blazor 应用程序
在这里,您可以看到左侧的侧边栏(来自MainLayout.razor的第 4 个代码片段)和中间加载的主体内容。当 Web 应用首次运行时,它会重定向到根 URL (/
) 。在我们的演示应用中,这由Home.razor表示,如图 7 中的项目 5 所示。
它看起来是这样的:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
代码片段 5:Home.razor
现在,让我们更改用户请求并点击侧边栏中的天气按钮。您会注意到 URL 更改为"https://localhost:7106/weather"。
此时,App.razor将要求Router查找具有“@page /weather”路由的组件,并将其替换到 body 部分。为此,我们有一个Weather.razor组件,其中包含一些静态数据。
@page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
代码片段 6:Weather.razor
如您所见,这个Weather.razor组件现在位于中心,取代了Home.razor组件。
图片 9:天气页面加载
让我检查一下该元素并向您展示它如何在浏览器中显示为单个页面。
图 10:检查天气页面上的元素
中间是 Weather.razor的代码,上方是几个
,其中一个带有类“page”,内部带有类“sidebar”。这些来自 MainLayout.razor的第 3行和第 4行。因此,最终,所有这些组件组合在一起形成一个页面。看看下面的 GIF。请注意,单击主页和天气页面只会替换正文。
GIF 1:在主页和天气页面之间导航
各位,所有关于SSR(服务器端渲染)的讨论都归结为这一点。它是一种 Blazor 托管模型,应用程序在服务器上运行,UI 更新通过SignalR 连接发送到客户端。这正是我们在本文中看到的:每次用户请求网页时,它都会在服务器上生成并发送到客户端,然后在浏览器中呈现。
##结论
Blazor 提供了一个强大的框架,用于使用基于组件的架构构建单页应用程序。您可以创建高度交互的 Web 应用程序。关键在于了解 Blazor 如何处理用户请求并动态更新内容而无需重新加载整个页面,从而提供流畅的用户体验。