Blazor 路由和导航的开发使用指南
检查传入的请求 URL 并将它们路由到适当的视图或页面是每个单页应用程序 (SPA) 框架的基本功能。 Blazor Server 和 WebAssembly 应用程序还支持使用一些内置组件和服务进行路由。 在本教程中,我将介绍在 Blazor 应用中实现路由所需的所有内容。
Blazor 应用中的路由配置
在开始为不同的 Blazor 组件/页面创建路由之前,我们需要了解 Blazor 服务器应用程序如何集成到 ASP.NET Core 端点路由中。 Blazor 服务器应用程序通过 SignalR 连接与客户端通信,并接受 Blazor 组件的传入连接,我们在 Startup.cs 文件中调用 MapBlazorHub 方法配置方法,如下所示:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
默认配置将所有请求路由到 Razor 页面,该页面充当 Blazor 服务器应用程序服务器端部分的主机。 按照惯例,此主机页面是 _Host.cshtml,它位于应用程序的 Pages 文件夹中。 主机文件中指定的路由称为回退路由,并且在路由匹配中具有非常低的优先级,这意味着当没有其他路由匹配时,将使用此路由。
Blazor 路由器组件简介
Router 组件是 Blazor 中的内置组件之一,用于 Blazor 应用程序的 App 组件中。 此组件启用 Blazor 应用程序中的路由并提供与当前导航状态相对应的路由数据。 该组件拦截传入的请求并呈现与请求的 URL 匹配的页面。
<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>
下表显示了路由器组件的属性。
属性 | 说明 |
---|---|
AdditionalAssemblies | 获取或设置一组附加程序集,应搜索这些程序集以查找可以匹配 URI 的组件。 |
AppAssembly | 获取或设置应搜索与 URI 匹配的组件的程序集。 |
Found | 获取或设置在为请求的路由找到匹配项时显示的内容。 |
Navigating | 获取或设置异步导航进行时要显示的内容。 |
NotFound | 获取或设置当没有找到所请求路由的匹配项时显示的内容。 |
OnNavigateAsync | 获取或设置在导航到新页面之前应调用的处理程序。 |
编译 Blazor 组件 (.razor) 时,它们生成的 C# 类保存在 obj\Debug\net5.0\Razor\Pages 文件夹中
如果您打开任何已编译的文件,您会注意到在编译后,所有带有 @page 指令的组件都生成了一个带有 RouteAttribute 属性的类。
应用程序启动时,会扫描 AppAssembly 属性指定的程序集,以从所有指定了 RouteAttribute 的类中收集路由信息。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
如果您创建了单独的组件类库,并且希望应用程序从这些程序集中扫描和加载路由,那么您可以使用 AdditionalAssemblies 属性来接受程序集对象的集合。
下面是从组件类库中定义的两个可路由组件 Component1 和 Component2 加载路由信息的示例。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"
AdditionalAssemblies="new[] { typeof(Component1).Assembly, typeof(Component2).Assembly }">
</Router>
在运行时,RouteView 组件从路由器接收 RouteData 以及任何路由参数,并使用组件中定义的布局呈现指定的组件。 如果未定义布局,则使用 DefaultLayout 属性指定的布局。 默认布局通常是共享文件夹中可用的 MainLayout 组件,但您也可以创建和指定自定义布局。
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
Found 模板用于显示找到匹配路由的内容,如下例所示,其中找到匹配路由并在浏览器中呈现计数器页面。
NotFound 模板用于在没有找到匹配的路由时显示内容。 默认情况下,NotFound 模板仅显示一条消息,如下面的屏幕截图所示。
我们还可以创建自定义错误布局和页面并显示自定义错误页面。 让我们在 Shared 文件夹中创建一个名为 ErrorLayout.razor 的新自定义布局。
ErrorLayout.razor
@inherits LayoutComponentBase
<main role="main" class="container">
<div class="text-center">
@Body
</div>
</main>
然后把LayoutView组件的Layout属性改成ErrorLayout,把LayoutView里面的内容改成如下
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(ErrorLayout)">
<h1 class="display-1">404</h1>
<h1 class="display-4">Not Found</h1>
<p class="lead">
Oops! Looks like this page doesn't exist.
</p>
</LayoutView>
</NotFound>
</Router>
如果您将在浏览器中运行该应用程序并尝试访问未在应用程序中任何位置指定的 URL,那么您将看到一个自定义 404 错误页面,如下所示。
所有 Blazor 应用程序都应将 PreferExactMatches 属性显式设置为 @true,以便路由匹配更喜欢精确匹配而不是通配符。 根据 Microsoft 官方文档,从 .NET 6 开始,此属性将不可用,并且路由器将始终更喜欢精确匹配。
定义路由、参数和约束
在我们学习如何为 Blazor 组件定义路由之前,我们需要确保我们在每个页面上都有以下可用的基本标记来正确解析 URL。 如果您正在创建 Blazor 服务器应用程序,则可以在 Pages/_Host.cshtml 文件的 head 部分添加此标记,对于 Blazor WebAssembly 应用程序,可以在 wwwroot/index.html 文件中添加此标记。
<base href="~/" />
要定义路由,我们可以使用 @page 指令,如下面的 Counter 组件示例所示。
@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++;
}
}
我们现在可以使用 /counter URL 访问计数器组件。
我们还可以使用多个@page 指令定义多个路由模板,如下例所示。
@page "/counter"
@page "/mycounter"
这意味着现在也可以使用 /mycounter URL 访问相同的 Counter 组件:
使用路由参数和 Blazor 路由模板支持参数将数据从一个页面传递到另一个页面是非常常见的做法。 路由参数名称不区分大小写,一旦我们定义了路由参数,路由器就会自动填充具有相同名称的相应组件属性。 例如,在下面的代码片段中,我们定义了一个路由参数 title 并在组件中创建了一个相应的属性 Title。 此属性将自动填充路由参数文本的值。 然后,我们将 Title 属性显示为 h1 元素内的页面标题。
@page "/counter/{title}"
<h1>@Title</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public string Title { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
运行应用程序并尝试在地址栏中指定 /counter/ 之后的任何字符串,您将看到路由参数值显示为页面标题。
我们还可以定义可选的路由参数,如下例所示,其中标题是可选参数,因为它在参数名称后面有问号 (?)。 如果我们不提供此路由参数的值,则该参数将在 OnInitialized 方法中使用 Counter 的默认值进行初始化。
@page "/counter/{title?}"
<h1>@Title</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public string Title { get; set; }
protected override void OnInitialized()
{
Title = Title ?? "Counter";
}
private void IncrementCount()
{
currentCount++;
}
}
Blazor 还支持在路由上强制执行类型匹配的路由约束。 在下面的代码片段中,我创建了一个以 int 类型开头的路由参数,这意味着现在我只能为此路由参数提供整数值。 计数器现在将从路由参数中指定的值开始。
@page "/counter/{start:int}"
<h1>Counter</h1>
<p>Current count: @Start</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int Start { get; set; }
private void IncrementCount()
{
Start++;
}
}
在浏览器中运行应用程序并在 URL 中指定任何整数值,例如 /counter/4,您将看到计数器将从该起始值开始递增。
下表显示了 Blazor 路由约束支持的类型。
Constraint | Example | Example Matches |
---|---|---|
bool | true, FALSE | |
datetime | 2016-12-31, 2016-12-31 7:32pm | |
decimal | 49.99, -1,000.01 | |
double | 1.234, -1,001.01e8 | |
float | 1.234, -1,001.01e8 | |
guid | CD2C1638-1638-72D5-1638-DEADBEEF1638, | |
int | 123456789, -123456789 | |
long | 123456789, -123456789 |
也可以定义多个路由参数,如下例所示,我们将 start 和 increment 定义为 int 类型参数。
@page "/counter/{start:int}/{increment:int}"
<h1>Counter</h1>
<p>Current count: @Start</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int Start { get; set; }
[Parameter]
public int Increment { get; set; }
private void IncrementCount()
{
Start+=Increment;
}
}
运行应用程序并在地址 URL 中指定起始值和增量值,如下所示,您会注意到计数器不仅以值 2 开始,而且每次单击 [外链图片转存中...(img-Ax5NtNoK-1626849711493)]
按钮时它也会增加 3。
运行应用程序并在地址 URL 中指定起始值和增量值,如下所示,您会注意到计数器不仅以值 2 开始,而且每次单击 Click me 按钮时它也会增加 3。
Blazor NavigationManager 服务概述
NavigationManager 服务允许我们在 C# 代码中管理 URI 和导航。 NavigationManager 类具有以下通用属性、方法和事件。
导航 | 类型 | 描述 |
---|---|---|
BaseUri | 属性 | 获取或设置当前的基 URI。 BaseUri 始终表示为带有尾部斜杠的字符串形式的绝对 URI。 通常,这对应于文档 元素上的“href”属性。 |
Uri | 属性 | 获取或设置当前 URI。 Uri 始终表示为字符串形式的绝对 URI。 |
NavigateTo | 方法 | 导航到指定的 URI。 |
ToAbsoluteUri | 方法 | 将相对 URI 转换为绝对 URI。 |
ToBaseRelativePath | 方法 | 给定一个基本 URI(例如,先前由 BaseUri 返回的 URI),将绝对 URI 转换为相对于基本 URI 前缀的 URI。 |
LocationChanged | 事件 | 当导航位置改变时触发的事件。 |
让我们创建一个页面来查看上面的一些属性和方法。 创建一个新的 Blazor 组件并使用 @inject 指令注入 NavigationManager 服务。 尝试在页面上打印 Uri 和 BaseUri 属性以查看它们返回的 URI 类型。
@page "/navigationmanager"
@inject NavigationManager nvm
<h3>Navigation Manager</h3>
<br />
<p>@nvm.Uri</p>
<p>@nvm.BaseUri</p>
运行该应用程序,您将在浏览器中看到类似于以下内容的输出。 Uri 属性将显示页面的当前绝对 URI,而 BaseUri 属性将显示当前基本 URI。
在页面上添加两个按钮 Home Page 和 Counter Page 并在@code 块中添加它们的 onclick 事件处理程序方法。 在事件处理程序方法中,我们可以使用 NavigateTo 方法将用户从 C# 代码重定向到不同的 Blazor 组件。
@page "/navigationmanager"
@inject NavigationManager nvm
<h3>Navigation Manager</h3>
<br />
<p>@nvm.Uri</p>
<p>@nvm.BaseUri</p>
<button class="btn btn-primary" @onclick="GoToHome">
Home Page
</button>
<button class="btn btn-primary" @onclick="GoToCounter">
Counter Page
</button>
@code {
private void GoToHome()
{
nvm.NavigateTo("/");
}
private void GoToCounter()
{
nvm.NavigateTo("counter");
}
}
运行应用程序并尝试同时单击两个按钮,您将能够按预期导航到主页和计数器页面。
如果不想以编程方式处理导航并想在 HTML 中生成超链接,则可以使用 Blazor NavLink 组件。 NavLink 组件类似于 HTML <a> 元素,具有一些很酷的功能。 如果 href 属性值与当前 URL 匹配,它会自动切换活动类与元素。 这允许我们在当前选择的链接上应用不同的样式。 你可以在 Shared/NavMenu.razor 文件中看到这个组件的用法
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
</div>
该组件还有一个 Match 属性,可以设置为以下之一:
- NavLinkMatch.All:当它匹配整个当前 URL 时,NavLink 处于活动状态。
- NavLinkMatch.Prefix(默认):NavLink 在匹配当前 URL 的任何前缀时处于活动状态。
总结概括
在本文章中,我尝试介绍 Blazor 应用程序中可用的许多路由功能,还介绍了开发人员可用的不同路由相关组件和服务。 我希望您现在能够更有信心地定义路由、参数和约束。 如果您喜欢本教程,请与他人分享以传播知识。
您的资助是我最大的动力!
金额随意,欢迎来赏!