翻译自 Waqas Anwar 2021年4月2日的文章 《A Developer’s Guide To Blazor Routing and Navigation》 [1]

检查传入的请求 URL 并将它们导航到对应的视图或页面是每个单页应用程序 (SPA) 框架的基本功能。Blazor Server 和 WebAssembly 应用程序也同样支持使用一些内置组件和服务进行路由。在本教程中,我将向您介绍在 Blazor 应用程序中实现路由所需了解的所有内容。

Blazor 应用程序中的路由配置

在开始为不同的 Blazor 组件/页面创建路由之前,我们需要了解如何将 Blazor Server 应用程序集成到 ASP.NET Core Endpoint 路由中。Blazor Server 应用程序通过 SignalR 连接与客户端进行通信,为了接受 Blazor 组件传入的连接,我们在 Startup.cs 文件的 Configure 方法中调用了 MapBlazorHub 方法,如下所示:

app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});

默认配置将所有请求都转发到一个 Razor 页面,该页面扮演 Blazor Server 应用程序服务端主机的角色。按照惯例,该主页是 _Host.cshtml,它位于应用程序的 Pages 文件夹中。该主文件中指定的路由称之为应急路由,在路由匹配中具有极低的优先级,这意味着当没有其他路由匹配时,才会使用该路由。

Blazor 路由组件介绍

Router[2] 组件是 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>

下表显示了 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 属性来接受一个 Assembly 对象集合。

下面是一个从定义在组件类库中的两个可路由组件(Component1Component2)加载路由信息的示例。

<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"
AdditionalAssemblies="new[] { typeof(Component1).Assembly, typeof(Component2).Assembly }">
</Router>

在运行时,RouteView 组件从 Router 接收 RouteData 以及任意路由参数,并使用组件中定义的布局渲染指定的组件。如果未定义布局,则使用 DefaultLayout 属性指定的布局。默认的布局通常是 Shared 文件夹中的 MainLayout 组件,不过您也可以创建并指定一个自定义布局。

<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />

Found 模板用于在找到匹配的路由时显示其内容,正如您在下图中所看到的那样,其中找到了一个匹配路由,并在浏览器中呈现了一个 Counter 页面。

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 组件定义路由之前,我们需要确保下面的 base 标签在每个页面都可用,以便正确地解析 URL。如果创建的是 Blazor Server 应用程序,那么您可以将此标签添加到 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 访问 Counter 组件了。

我们还可以使用多个 @page 指令定义多个路由模板,如下面例所示。

@page "/counter"
@page "/mycounter"

这意味着现在也可以使用 /mycounter URL 访问同一个 Counter 组件:

使用路由参数将数据从一个页面传递到另一个页面是十分常见的做法,Blazor 路由模板支持路由参数。路由参数名称不区分大小写,一旦我们定义了路由参数,路由器就会自动填充对应的具有相同名称的组件属性。例如,在下面的代码片段中,我们在组件中定义了一个路由参数 title,并创建了一个对应的属性 Title。此属性将自动使用路由参数文本的值填充。然后,我们在 h1 元素中显示 Title 属性作为页面的标题。

@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/ 之后指定任意的字符串,您将看到路由参数的值会显示为页面标题。

我们还可以定义可选的路由参数,如下例所示,其中 title 是可选参数,因为在此参数名称后面带有问号 (?)。假如我们不提供此路由参数的值,该参数将在 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 类型的路由参数 start,这意味着现在我只能为此路由参数提供整数值。计数器现在将以路由参数中指定的值开始计数。

@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 路由约束支持的类型。

约束 示例 匹配项示例
bool {active:bool} true,FALSE
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm
decimal {price:decimal} 49.99, -1,000.01
double {weight:double} 1.234, -1,001.01e8
float {weight:float} 1.234, -1,001.01e8
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638, {CD2C1638-1638-72D5-1638-DEADBEEF1638}
int {id:int} 123456789, -123456789
long {ticks:long} 123456789, -123456789

还可以定义多个路由参数,如下例所示,我们将 startincrement 定义为 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 地址中指定 startincrement 的值,您会注意到,当您每次点击 Click me 按钮时,计数器不仅会以数字 2 开始计数,而且会以 3 递增。

Blazor NavigationManager 服务概述

NavigationManager 服务允许我们在 C# 代码中管理 URI 和导航。NavigationManager 类具有以下常见的属性、方法和事件。

名称 类型 说明
BaseUri 属性 获取或设置当前的基 URI。BaseUri 始终表示为字符串形式的绝对 URI,以斜杠结尾。 通常,这与文档中 <base> 元素的 href 特性相对应。
Uri 属性 获取或设置当前 URI。 Uri 始终以字符串形式表示为绝对 URI。
NavigateTo 方法 导航到指定 URI。
ToAbsoluteUri 方法 将相对 URI 转换为绝对 URI。
ToBaseRelativePath 方法 给定基 URI (比如,前面的 BaseUri 的返回值),将绝对 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 PageCounter Page,并在 @code 代码块中添加它们的 onclick 事件处理方法。在事件处理方法中,我们可以在 C# 代码中使用 NavigateTo 方法将用户重定向到其它的 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> 元素,具有一些很酷的功能。如果 NavLink 的 href 特性值与当前的 URL 相匹配,则会自动切换该元素的 active CSS 类(class)。这就使得我们可以在当前选中的链接上应用不同的样式。您可以在 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>

NavLink 组件还有一个 Match 属性,可以设置为以下选项之一:

  • NavLinkMatch.All:指定当 NavLink 与整个当前 URL 匹配时应处于活动状态。
  • NavLinkMatch.Prefix(默认值):指定当 NavLink 与当前 URL 的任意前缀匹配时应处于活动状态。

Match 属性:获取或设置一个值,该值表示 URL 匹配行为。

总结

在本教程中,我尝试介绍 Blazor 应用程序中的多种路由功能,还介绍了开发者可用的与路由相关的一些组件和服务。我希望您现在能够更熟练地定义路由、参数和约束。如果您喜欢本教程,请与他人分享以传播知识。

相关阅读:

作者 : Waqas Anwar

翻译 : 技术译站

链接 : 英文原文


  1. https://www.ezzylearning.net/tutorial/a-developers-guide-to-blazor-routing-and-navigation A Developer’s Guide To Blazor Routing and Navigation

  2. https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.components.routing.router

Blazor 路由及导航开发指南的更多相关文章

  1. Blazor 模板化组件开发指南

    翻译自 Waqas Anwar 2021年4月15日的文章 <A Developer's Guide To Blazor Templated Components> [1] 在我之前的一篇 ...

  2. Blazor 组件库开发指南

    翻译自 Waqas Anwar 2021年5月21日的文章 <A Developer's Guide To Blazor Component Libraries> [1] Blazor 的 ...

  3. Blazor 数据绑定开发指南

    翻译自 Waqas Anwar 2021年3月21日的文章 <A Developer's Guide to Blazor Data Binding> [1] 现如今,大多数 Web 应用程 ...

  4. Blazor 事件处理开发指南

    翻译自 Waqas Anwar 2021年3月25日的文章 <A Developer's Guide To Blazor Event Handling> [1] 如果您正在开发交互式 We ...

  5. nodejs开发指南读后感

    nodejs开发指南读后感 阅读目录 使用nodejs创建http服务器; supervisor的使用及nodejs常见的调式代码命令了解; 了解Node核心模块; ejs模板引擎 Express 理 ...

  6. AngularJS开发指南16:AngularJS构建大型Web应用详解

    AngularJS是由Google创建的一种JS框架,使用它可以扩展应用程序中的HTML功能,从而在web应用程序中使用HTML声明动态内容.在该团队工作的软件工程师Brian Ford近日撰写了一篇 ...

  7. Android SDK 开发指南

    Android SDK 开发指南 视频详解 以下视频是对融云 Android SDK 开发使用的详细讲解,您可以在阅读文档时配合学习.   更多视频教程如下: CSDN 融云 Android SDK ...

  8. JVM 平台上的各种语言的开发指南

    JVM 平台上的各种语言的开发指南 为什么我们需要如此多的JVM语言? 在2013年你可以有50中JVM语言的选择来用于你的下一个项目.尽管你可以说出一大打的名字,你会准备为你的下一个项目选择一种新的 ...

  9. ECSHOP二次开发指南

    ECSHOP二次开发指南 发布时间:2013-05-28 12:47:00   来源:   评论:0 点击: 次 [字号:大 中 小] QQ空间新浪微博腾讯微博人人网豆瓣网百度空间百度搜藏开心网复制更 ...

随机推荐

  1. 【Azure API 管理】APIM集成内网虚拟网络后,启用自定义路由管理外出流量经过防火墙(Firewall),遇见APIs加载不出来问题

    问题描述 使用 Azure 虚拟网络,Azure APIM 可以管理无法通过 Internet 访问的 API,达到以保护企业内部的后端API的目的.在虚拟网络中,启用网络安全组(NSG:Networ ...

  2. http强制缓存、协商缓存、指纹ETag详解

    目录 实操目录及步骤 缓存分类 强制缓存 对比缓存 指纹 Etag 摘要及加密算法 缓存总结 每个浏览器都有一个自己的缓存区,使用缓存区的数据有诸多好处,减少冗余的数据传输,节省网络传输.减少服务器负 ...

  3. 高性能的Redis数据结构小结

    一.概述 Redis 作为一种 KV 缓存服务器,有着极高的性能,相对于 Memcache,Redis 支持更多种数据类型,因此在业界应用广泛. 记得刚毕业那会参加面试,面试官会问我 Redis 为什 ...

  4. CSS3转换(transform)基本用法介绍

    一个炫酷的网页离不开css的transform.transition.animation三个属性,之前一直没有涉及到这块内容,刚好最近要做一个相关东西,趁此机会好好学一学这三个属性. 一.功能 实现元 ...

  5. Pptx的多路径形状转为WPF的Path

    本文是将演示如何解析pptx文件的多路径的形状转换到WPF,绘制多个Shape的Path Shape Path 这是Pptx的[标注:弯曲曲线(无边框)]形状的OpenXml定义部分: <cal ...

  6. 柔性数组(Redis源码学习)

    柔性数组(Redis源码学习) 1. 问题背景 在阅读Redis源码中的字符串有如下结构,在sizeof(struct sdshdr)得到结果为8,在后续内存申请和计算中也用到.其实在工作中有遇到过这 ...

  7. Retrofit使用Kotlin协程发送请求

    Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用.本文简单介绍一下Retrofit中协程的使用 导入依赖 app的build文件中加入: impleme ...

  8. Blazor 组件入门指南

    翻译自 Waqas Anwar 2021年3月19日的文章 <A Beginner's Guide to Blazor Components> [1] Blazor 应用程序是组件的组合, ...

  9. python之struct详解

    python之struct详解 2018-05-23 18:20:29 醉小义 阅读数 20115更多 分类专栏: python   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议 ...

  10. Quartz和Spring Task定时任务的简单应用和比较

    看了两个项目,一个用的是Quartz写的定时器,一个是使用spring的task写的,网上看了2篇文章,写的比较清楚,这里做一下留存 链接一.菠萝大象:http://www.blogjava.net/ ...