Blazor 路由及導航開發(fā)指南
本文轉(zhuǎn)載自微信公眾號「技術譯站」,作者技術譯民 。轉(zhuǎn)載本文請聯(lián)系技術譯站公眾號。
翻譯自 Waqas Anwar 2021年4月2日的文章 《A Developer’s Guide To Blazor Routing and Navigation》 [1]
檢查傳入的請求 URL 并將它們導航到對應的視圖或頁面是每個單頁應用程序 (SPA) 框架的基本功能。Blazor Server 和 WebAssembly 應用程序也同樣支持使用一些內(nèi)置組件和服務進行路由。在本教程中,我將向您介紹在 Blazor 應用程序中實現(xiàn)路由所需了解的所有內(nèi)容。
Blazor 應用程序中的路由配置
在開始為不同的 Blazor 組件/頁面創(chuàng)建路由之前,我們需要了解如何將 Blazor Server 應用程序集成到 ASP.NET Core Endpoint 路由中。Blazor Server 應用程序通過 SignalR 連接與客戶端進行通信,為了接受 Blazor 組件傳入的連接,我們在 Startup.cs 文件的 Configure 方法中調(diào)用了 MapBlazorHub 方法,如下所示:
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapBlazorHub();
- endpoints.MapFallbackToPage("/_Host");
- });
默認配置將所有請求都轉(zhuǎn)發(fā)到一個 Razor 頁面,該頁面扮演 Blazor Server 應用程序服務端主機的角色。按照慣例,該主頁是 _Host.cshtml,它位于應用程序的 Pages 文件夾中。該主文件中指定的路由稱之為應急路由,在路由匹配中具有極低的優(yōu)先級,這意味著當沒有其他路由匹配時,才會使用該路由。
Blazor 路由組件介紹
Router[2] 組件是 Blazor 中的內(nèi)置組件之一,用在 Blazor 應用程序的 App 組件之中。該組件啟用了 Blazor 應用程序中的路由,并提供與當前導航狀態(tài)相對應的路由數(shù)據(jù)。它攔截傳入的請求并呈現(xiàn)與請求 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 組件的屬性。
當編譯 Blazor 組件 (.razor) 時,它們生成的 C# 類會保存在 obj\Debug\net5.0\Razor\Pages 文件夾中。
如果您打開任意一個已編譯的文件,將會注意到在編譯之后,所有帶有 @page 指令的組件都生成了一個帶有 RouteAttribute 特性的類。
當應用程序啟動時,會掃描通過 AppAssembly 屬性指定的程序集,從所有指定了 RouteAttribute 特性的類中收集路由信息。
- <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
如果您創(chuàng)建了獨立的組件類庫,并希望應用程序從這些程序集中掃描和加載路由,那么您可以使用 AdditionalAssemblies 屬性來接受一個 Assembly 對象集合。
下面是一個從定義在組件類庫中的兩個可路由組件(Component1 和 Component2)加載路由信息的示例。
- <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"
- AdditionalAssemblies="new[] { typeof(Component1).Assembly, typeof(Component2).Assembly }">
- </Router>
在運行時,RouteView 組件從 Router 接收 RouteData 以及任意路由參數(shù),并使用組件中定義的布局渲染指定的組件。如果未定義布局,則使用 DefaultLayout 屬性指定的布局。默認的布局通常是 Shared 文件夾中的 MainLayout 組件,不過您也可以創(chuàng)建并指定一個自定義布局。
Found 模板用于在找到匹配的路由時顯示其內(nèi)容,正如您在下圖中所看到的那樣,其中找到了一個匹配路由,并在瀏覽器中呈現(xiàn)了一個 Counter 頁面。
NotFound 模板用于在沒有找到匹配的路由時顯示內(nèi)容。默認情況下,NotFound 模板僅顯示一條消息,如下面的截圖所示。
我們還可以創(chuàng)建自定義錯誤的布局和頁面,以顯示自定義錯誤頁面。讓我們在 Shared 文件夾中創(chuàng)建一個新的名為 ErrorLayout.razor 的自定義布局。
ErrorLayout.razor
- @inherits LayoutComponentBase
- <main role="main" class="container">
- <div class="text-center">
- @Body
- </div>
- </main>
然后將 LayoutView 組件的 Layout 屬性改為 ErrorLayout,并將 LayoutView 里的內(nèi)容修改如下:
- <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>
現(xiàn)在,如果您在瀏覽器中運行應用程序,并嘗試訪問一個未在應用中任何位置指定過的 URL,那么您將會看到一個自定義的 404 錯誤頁面,如下所示。
所有 Blazor 應用程序都應將 PreferExactMatches 特性顯式地設置為 @true,以便路由匹配更傾向于精確匹配,而不是通配符匹配。根據(jù) Microsoft 官方文檔,此特性從 .NET 6 開始將不可用,路由器將總是更傾向于精確匹配。
定義路由、參數(shù)和約束
在我們學習如何為 Blazor 組件定義路由之前,我們需要確保下面的 base 標簽在每個頁面都可用,以便正確地解析 URL。如果創(chuàng)建的是 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++;
- }
- }
現(xiàn)在我們就可以使用 /counter URL 訪問 Counter 組件了。
我們還可以使用多個 @page 指令定義多個路由模板,如下面例所示。
- @page "/counter"
- @page "/mycounter"
這意味著現(xiàn)在也可以使用 /mycounter URL 訪問同一個 Counter 組件:
使用路由參數(shù)將數(shù)據(jù)從一個頁面?zhèn)鬟f到另一個頁面是十分常見的做法,Blazor 路由模板支持路由參數(shù)。路由參數(shù)名稱不區(qū)分大小寫,一旦我們定義了路由參數(shù),路由器就會自動填充對應的具有相同名稱的組件屬性。例如,在下面的代碼片段中,我們在組件中定義了一個路由參數(shù) title,并創(chuàng)建了一個對應的屬性 Title。此屬性將自動使用路由參數(shù)文本的值填充。然后,我們在 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/ 之后指定任意的字符串,您將看到路由參數(shù)的值會顯示為頁面標題。
我們還可以定義可選的路由參數(shù),如下例所示,其中 title 是可選參數(shù),因為在此參數(shù)名稱后面帶有問號 (?)。假如我們不提供此路由參數(shù)的值,該參數(shù)將在 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 還支持路由約束,在路由上強制類型匹配。在下面的代碼片段中,我創(chuàng)建了一個 int 類型的路由參數(shù) start,這意味著現(xiàn)在我只能為此路由參數(shù)提供整數(shù)值。計數(shù)器現(xiàn)在將以路由參數(shù)中指定的值開始計數(shù)。
- @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 中指定任一整數(shù)值,比如 /counter/4,您會看到計數(shù)器將以該起始值遞增。
下表顯示了 Blazor 路由約束支持的類型。
還可以定義多個路由參數(shù),如下例所示,我們將 start 和 increment 定義為 int 類型的參數(shù)。
- @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 地址中指定 start 和 increment 的值,您會注意到,當您每次點擊 Click me 按鈕時,計數(shù)器不僅會以數(shù)字 2 開始計數(shù),而且會以 3 遞增。
Blazor NavigationManager 服務概述
NavigationManager 服務允許我們在 C# 代碼中管理 URI 和導航。NavigationManager 類具有以下常見的屬性、方法和事件。
讓我們來創(chuàng)建一個頁面,查看一下以上屬性和方法的一些實際行為。創(chuàng)建一個新的 Blazor 組件并使用 @inject 指令注入 NavigationManager 服務。嘗試在頁面上打印出 Uri 和 BaseUri 屬性,來查看一下它們返回的是什么類型的 URI。
- @page "/navigationmanager"
- @inject NavigationManager nvm
- <h3>Navigation Manager</h3>
- <br />
- <p>@nvm.Uri</p>
- <p>@nvm.BaseUri</p>
運行應用程序,您將在瀏覽器中看到類似以下內(nèi)容的輸出。Uri 屬性顯示當前頁面的絕對 URI,而 BaseUri 屬性顯示當前的基 URI。
在頁面上添加兩個按鈕 Home Page 和 Counter 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");
- }
- }
運行應用程序并試著點擊這兩個按鈕,將按預期的那樣,您可以導航到主頁和計數(shù)器頁面。
如果不想以編程方式處理導航,而想在 HTML 中生成超鏈接,則可以使用 Blazor NavLink 組件。NavLink 組件類似于 HTML 中的 元素,具有一些很酷的功能。如果 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 匹配時應處于活動狀態(tài)。
- NavLinkMatch.Prefix(默認值):指定當 NavLink 與當前 URL 的任意前綴匹配時應處于活動狀態(tài)。
Match 屬性:獲取或設置一個值,該值表示 URL 匹配行為。
總結(jié)
在本教程中,我嘗試介紹 Blazor 應用程序中的多種路由功能,還介紹了開發(fā)者可用的與路由相關的一些組件和服務。我希望您現(xiàn)在能夠更熟練地定義路由、參數(shù)和約束。如果您喜歡本教程,請與他人分享以傳播知識。