ASP.NET 核心 Blazor 路由和導覽
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
本文說明如何管理 Blazor 應用程式要求路由,以及如何使用 NavLink 元件來建立導覽連結。
重要
本文中的程式碼範例會顯示在 Navigation
上呼叫的方法,其為在類別和元件中插入的 NavigationManager。
靜態與互動式路由
本章節適用 Blazor Web App。
如果已啟用預先轉譯,Blazor 路由器 (Router
元件,Routes.razor
中的 <Router>
) 會在靜態伺服器端轉譯 (靜態 SSR) 期間執行靜態路由至元件。 這種類型的路由稱為 靜態路由。
當互動式轉譯模式指派給 Routes
元件時,在伺服器上使用靜態路由進行靜態 SSR 之後,Blazor 路由器會變成互動式。 這種類型的路由稱為 互動式路由。
靜態路由器會使用端點路由和 HTTP 要求路徑來判斷要轉譯的元件。 當路由器變成互動式時,它會使用文件的 URL (瀏覽器網址列中的 URL) 來判斷要轉譯的元件。 這表示,如果文件的 URL 動態變更為另一個有效的內部 URL,互動式路由器可以動態變更轉譯的元件,而且不需要執行 HTTP 要求來擷取新的頁面內容,就可以這麼做。
互動式路由也會防止預先轉譯,因為未透過一般頁面要求向伺服器要求新的頁面內容。 如需詳細資訊,請參閱預先轉譯 ASP.NET Core Razor 元件。
路由範本
Router 元件可讓您路由至 Razor 元件,且位於應用程式的 Routes
元件 (Components/Routes.razor
) 中。
編譯具有 @page
指示詞的 Razor 元件 (.razor
) 時,會為產生的元件類別提供一個 RouteAttribute,指定元件的路由範本。
當應用程式啟動時,會掃描指定為路由器 AppAssembly
的組件,以收集具有 RouteAttribute 的應用程式元件的路由資訊。
在執行階段,RouteView 元件:
選擇性地為未使用 @layout
指示詞指定配置的元件指定具有配置類別的 DefaultLayout 參數。 架構 Blazor 的專案範本會將 MainLayout
元件 (MainLayout.razor
) 指定為應用程式的預設配置。 如需配置的詳細資訊,請參閱 ASP.NET Core Blazor 配置。
元件支援使用多個 @page
指示詞的多個路由範本。 下列範例元件會在 /blazor-route
和 /different-blazor-route
的要求上載入。
BlazorRoute.razor
:
@page "/blazor-route"
@page "/different-blazor-route"
<PageTitle>Routing</PageTitle>
<h1>Routing Example</h1>
<p>
This page is reached at either <code>/blazor-route</code> or
<code>/different-blazor-route</code>.
</p>
@page "/blazor-route"
@page "/different-blazor-route"
<PageTitle>Routing</PageTitle>
<h1>Routing Example</h1>
<p>
This page is reached at either <code>/blazor-route</code> or
<code>/different-blazor-route</code>.
</p>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
重要
若要讓 URL 正確解析,應用程式必須包含 <base>
標記 (<head>
內容的位置) 與在 href
屬性中指定的應用程式基底路徑。 如需詳細資訊,請參閱裝載和部署 ASP.NET Core Blazor。
除了使用 @page
指示詞將路由範本指定為字串常值,還可以使用 @attribute
指示詞來指定以常數為基礎的路由範本。
在下列範例中,元件中的 @page
指示詞會取代為 @attribute
指示詞,而 Constants.CounterRoute
中的常數型路由範本,其會在應用程式中的其他位置設定為 "/counter
":
- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]
注意
隨著 ASP.NET Core 5.0.1 的發行以及在任何其他 5.x 版中,Router
元件會包含設定為 @true
的 PreferExactMatches
參數。 如需詳細資訊,請參閱從 ASP.NET Core 3.1 移轉至 5.0。
將元素焦點放在導覽上
FocusOnNavigate 元件會在從某個頁面導覽到另一個頁面之後,根據 CSS 選取器,將 UI 焦點設定為元素。
<FocusOnNavigate RouteData="routeData" Selector="h1" />
當 Router 元件導覽至新頁面,FocusOnNavigate 元件會將焦點設定為頁面的最上層標頭 (<h1>
)。 這是確保使用螢幕助讀程式時宣告頁面導覽的常見策略。
找不到內容時提供自訂內容
如果找不到要求的路由內容,Router 元件可讓應用程式指定自訂內容。
在 Router 元件的 NotFound 參數中設定自訂內容:
<Router ...>
...
<NotFound>
...
</NotFound>
</Router>
支援任意項目做為 NotFound 參數的內容,例如其他互動式元件。 若要將預設配置套用至 NotFound 內容,請參閱 ASP.NET Core Blazor 配置。
重要
Blazor Web App 不會使用 NotFound 參數 (<NotFound>...</NotFound>
標記),但為了回溯相容性會支援該參數,以避免架構發生重大變更。 伺服器端 ASP.NET Core 中介軟體管線會處理伺服器上的要求。 使用伺服器端技術來處理不正確的要求。 如需詳細資訊,請參閱 ASP.NET Core Blazor 轉譯模式。
從多個組件路由至元件
本章節適用 Blazor Web App。
使用 Router 元件的 AdditionalAssemblies 參數和端點慣例建立器 AddAdditionalAssemblies 來探索其他組件中的可路由元件。 下列小節說明每個 API 的使用時機和方式。
靜態路由
若要從靜態伺服器端轉譯(靜態 SSR)的其他元件探索可路由元件,即使路由器稍後變成互動式轉譯的互動式元件,也必須向 Blazor 架構公開組件。 使用鏈結至伺服器專案 Program
檔案中 MapRazorComponents 的其他組件呼叫 AddAdditionalAssemblies 方法。
下列範例會使用專案的 _Imports.razor
檔案,在 BlazorSample.Client
專案的組件中包含可路由的元件:
app.MapRazorComponents<App>()
.AddAdditionalAssemblies(typeof(BlazorSample.Client._Imports).Assembly);
注意
上述指引也適用於 元件類別庫 案例。 如需類別庫和靜態 SSR 的其他重要指導,請參閱 具有靜態伺服器端轉譯 (static SSR) 的 ASP.NET CoreRazor 類別庫 (RCL)。
互動式路由
當互動式轉譯模式可以指派給 Routes
元件 (Routes.razor
) 時,這會使 Blazor 路由器在伺服器上使用靜態路由進行靜態 SSR 之後變成互動式。 例如,<Routes @rendermode="InteractiveServer" />
將互動式伺服器端轉譯 (互動式 SSR) 指派給 Routes
元件。 Router
元件會從 Routes
元件繼承互動式伺服器端轉譯 (互動式 SSR)。 路由器會在伺服器上的靜態路由之後變成互動式。
互動式路由的內部瀏覽並不涉及向伺服器要求新的頁面內容。 因此,內部頁面要求不會發生預先轉譯。 如需詳細資訊,請參閱預先轉譯 ASP.NET Core Razor 元件。
如果伺服器專案中定義了 Routes
元件,Router
元件的 AdditionalAssemblies 參數應該包含 .Client
專案的組件。 這可讓路由器在以互動方式轉譯時正確運作。
在下列範例中, Routes
元件位於伺服器專案中,而 BlazorSample.Client
專案的 _Imports.razor
檔案會指出要搜尋可路由元件的組件:
<Router
AppAssembly="..."
AdditionalAssemblies="new[] { typeof(BlazorSample.Client._Imports).Assembly }">
...
</Router>
除了指定給 AppAssembly 的組件之外,也會掃描其他組件。
注意
上述指引也適用於 元件類別庫 案例。
或者,可路由的元件只存在於套用全域互動式 WebAssembly 或自動轉譯的 .Client
專案中,而且 Routes
元件是在 .Client
專案中定義,而不是伺服器專案。 在此情況下,沒有具有可路由元件的外部組件,因此不需要為 AdditionalAssemblies 指定值。
本節適用 Blazor Server 應用程式。
使用 Router 元件的 AdditionalAssemblies 參數和端點慣例建立器 AddAdditionalAssemblies 來探索其他組件中的可路由元件。
在下列範例中, Component1
是在名為 ComponentLibrary
之參考 元件類別庫 中定義的可路由元件:
<Router
AppAssembly="..."
AdditionalAssemblies="new[] { typeof(ComponentLibrary.Component1).Assembly }">
...
</Router>
除了指定給 AppAssembly 的組件之外,也會掃描其他組件。
路由參數
路由器會使用路由參數,以相同名稱填入對應的元件參數。 路由參數名稱區分大小寫。 在下列範例中,text
參數會將路由區段的值指派給元件的 Text
屬性。 對 /route-parameter-1/amazing
提出要求時,內容會轉譯為 Blazor is amazing!
。
RouteParameter1.razor
:
@page "/route-parameter-1/{text}"
<PageTitle>Route Parameter 1</PageTitle>
<h1>Route Parameter Example 1</h1>
<p>Blazor is @Text!</p>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<PageTitle>Route Parameter 1</PageTitle>
<h1>Route Parameter Example 1</h1>
<p>Blazor is @Text!</p>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
}
支援選用的參數。 在下列範例中,text
選擇性參數會將路由區段的值指派給元件的 Text
屬性。 如果區段不存在,則 Text
的值會設定為 fantastic
。
不支援選用的參數。 在下列範例中,會套用兩個 @page
指示詞。 第一個指示詞會允許導覽至沒有路由參數的元件。 第二個指示詞會將 {text}
路由參數值指派給元件的 Text
屬性。
RouteParameter2.razor
:
@page "/route-parameter-2/{text?}"
<PageTitle>Route Parameter 2</PageTitle>
<h1>Route Parameter Example 2</h1>
<p>Blazor is @Text!</p>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnParametersSet() => Text = Text ?? "fantastic";
}
@page "/route-parameter-2/{text?}"
<PageTitle>Route Parameter 2</PageTitle>
<h1>Route Parameter Example 2</h1>
<p>Blazor is @Text!</p>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnParametersSet() => Text = Text ?? "fantastic";
}
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnParametersSet()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnParametersSet()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnParametersSet()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2"
@page "/route-parameter-2/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnParametersSet()
{
Text = Text ?? "fantastic";
}
}
當使用 OnInitialized{Async}
生命週期方法 而非 OnParametersSet{Async}
生命週期方法 時,如果使用者在相同的元件中瀏覽,就不會發生將 Text
屬性預設指派給 fantastic
的情況。 例如,當使用者從 /route-parameter-2/amazing
瀏覽至 /route-parameter-2
時,就會發生這種情況。 當元件執行個體保存並接受新參數時,不會再次叫用 OnInitialized
方法。
注意
路由參數不適用查詢字串值。 若要使用查詢字串,請參閱查詢字串小節。
路由條件約束
路由條件約束會在對元件的路由區段上強制執行型別比對。
在下列範例中,與 User
元件的路由只會在下列情況下比對:
- 要求 URL 中有
Id
路由區段。 Id
區段是整數 (int
) 型別。
User.razor
:
@page "/user/{Id:int}"
<PageTitle>User</PageTitle>
<h1>User Example</h1>
<p>User Id: @Id</p>
@code {
[Parameter]
public int Id { get; set; }
}
注意
路由條件約束不適用查詢字串值。 若要使用查詢字串,請參閱查詢字串小節。
下表所示的路由條件約束可供使用。 如需會比對不因文化特性而異的路由條件約束,請參閱下表中的警告以取得詳細資訊。
條件約束 | 範例 | 範例相符項目 | 不因 文化特性而異 比對 |
---|---|---|---|
bool |
{active:bool} |
% | 否 |
datetime |
{dob:datetime} |
% | 是 |
decimal |
{price:decimal} |
% | 是 |
double |
{weight:double} |
% | 是 |
float |
{weight:float} |
% | 是 |
guid |
{id:guid} |
% | 否 |
int |
{id:int} |
% | 是 |
long |
{ticks:long} |
% | 是 |
nonfile |
{parameter:nonfile} |
不是 BlazorSample.styles.css ,不是 favicon.ico |
是 |
警告
確認 URL 可以轉換成 CLR 類型的路由條件約束 (例如 int
或 DateTime) 一律使用不因國別而異的文化特性。 這些條件約束假設 URL 不可當地語系化。
路由條件約束也適用選用參數。 在下列範例中,Id
為必要,但 Option
為選用的布林路由參數。
User.razor
:
@page "/user/{id:int}/{option:bool?}"
<p>
Id: @Id
</p>
<p>
Option: @Option
</p>
@code {
[Parameter]
public int Id { get; set; }
[Parameter]
public bool Option { get; set; }
}
避免在路由參數中擷取檔案
下列路由範本無意中在其選擇性路由參數 (Optional
) 中擷取靜態資產路徑。 例如,擷取應用程式的樣式表單 (.styles.css
) 會破壞應用程式的樣式:
@page "/{optional?}"
...
@code {
[Parameter]
public string? Optional { get; set; }
}
若要將路由參數限制為擷取非檔案路徑,請在路由範本中使用 :nonfile
限制式 :
@page "/{optional:nonfile?}"
針對包含點的 URL 路由
伺服器端預設路由範本會假設,如果要求 URL 的最後一個區段包含點 (.
),則是要求檔案。 例如,相對 URL /example/some.thing
會由路由器解譯為要求名為 some.thing
的檔案。 若未進行額外的設定,如果 some.thing
是要路由至具有 @page
指示詞的元件,且 some.thing
為路由參數值,則應用程式會傳回 404 - 找不到回應。 若要使用路由搭配包含點的一或多個參數,應用程式必須使用自訂範本來設定路由。
考慮下列 Example
元件,其可以從 URL 的最後一個區段接收路由參數。
Example.razor
:
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string? Param { get; set; }
}
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string? Param { get; set; }
}
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string Param { get; set; }
}
@page "/example"
@page "/example/{param}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string Param { get; set; }
}
若要允許裝載 Blazor WebAssembly方案的 Server 應用程式路由 param
路由參數中具有點的要求,請在 Program
檔案中新增後援檔案路由範本與選用參數:
app.MapFallbackToFile("/example/{param?}", "index.html");
若要設定 Blazor Server 應用程式以路由 param
路由參數中具有點的要求,請在 Program
檔案中新增後援頁面路由範本與選用參數:
app.MapFallbackToPage("/example/{param?}", "/_Host");
如需詳細資訊,請參閱 ASP.NET Core 中的路線規劃。
若要允許裝載 Blazor WebAssembly方案的 Server 應用程式路由 param
路由參數中具有點的要求,請在 Startup.Configure
中新增後援檔案路由範本與選用參數。
Startup.cs
:
endpoints.MapFallbackToFile("/example/{param?}", "index.html");
若要設定 Blazor Server 應用程式以路由 param
路由參數中具有點的要求,請在 Startup.Configure
中新增後援頁面路由範本與選用參數。
Startup.cs
:
endpoints.MapFallbackToPage("/example/{param?}", "/_Host");
如需詳細資訊,請參閱 ASP.NET Core 中的路線規劃。
Catch-all 路由參數
元件中支援會跨多個資料夾界限擷取路徑的 Catch-all 路由參數。
Catch-all 路由參數為:
- 具名以符合路由區段名稱。 名稱不區分大小寫。
string
型別。 架構不提供自動轉換。- 在 URL 的結尾。
CatchAll.razor
:
@page "/catch-all/{*pageRoute}"
<PageTitle>Catch All</PageTitle>
<h1>Catch All Parameters Example</h1>
<p>Add some URI segments to the route and request the page again.</p>
<p>
PageRoute: @PageRoute
</p>
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
<PageTitle>Catch All</PageTitle>
<h1>Catch All Parameters Example</h1>
<p>Add some URI segments to the route and request the page again.</p>
<p>
PageRoute: @PageRoute
</p>
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string PageRoute { get; set; }
}
對於具有 /catch-all/{*pageRoute}
路由範本的 URL /catch-all/this/is/a/test
,PageRoute
的值會設定為 this/is/a/test
。
擷取路徑的斜線和區段會經過解碼。 針對 /catch-all/{*pageRoute}
的路由範本,URL /catch-all/this/is/a%2Ftest%2A
會產生 this/is/a/test*
。
URI 和導覽狀態協助程式
使用 NavigationManager 來管理 C# 程式碼中的 URI 和導覽。 NavigationManager 提供下表所示的事件和方法。
member | 描述 |
---|---|
Uri | 取得目前的絕對 URI。 |
BaseUri | 取得基底 URI (具有尾端斜線),其可加在相對 URI 路徑前面以產生絕對 URI。 一般而言,BaseUri 會對應至文件 <base> 元素上的 href 屬性 (<head> 內容的位置)。 |
NavigateTo | 導覽至指定的 URI。 如果 forceLoad 為 false :
forceLoad 為 true :
如需詳細資訊,請參閱增強式導覽和表單處理一節。 如果 |
LocationChanged | 導覽位置變更時引發的事件。 如需詳細資訊,請參閱位置變更一節。 |
ToAbsoluteUri | 將相對 URI 轉換成絕對 URI。 |
ToBaseRelativePath | 根據應用程式的基底 URI,將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。 如需範例,請參閱產生相對於基底 URI 前置詞的 URI 一節。 |
RegisterLocationChangingHandler |
註冊處理常式來處理傳入導覽事件。 呼叫 NavigateTo 一律會叫用處理常式。 |
GetUriWithQueryParameter | 傳回更新 NavigationManager.Uri 而建構的 URI,並新增、更新或移除單一參數。 如需詳細資訊,請參閱查詢字串一節。 |
member | 描述 |
---|---|
Uri | 取得目前的絕對 URI。 |
BaseUri | 取得基底 URI (具有尾端斜線),其可加在相對 URI 路徑前面以產生絕對 URI。 一般而言,BaseUri 會對應至文件 <base> 元素上的 href 屬性 (<head> 內容的位置)。 |
NavigateTo | 導覽至指定的 URI。 如果 forceLoad 為 true :
replace 為 true ,則會取代瀏覽器歷程記錄中的目前 URI,而不是將新的 URI 推送至歷程記錄堆疊。 |
LocationChanged | 導覽位置變更時引發的事件。 如需詳細資訊,請參閱位置變更一節。 |
ToAbsoluteUri | 將相對 URI 轉換成絕對 URI。 |
ToBaseRelativePath | 根據應用程式的基底 URI,將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。 如需範例,請參閱產生相對於基底 URI 前置詞的 URI 一節。 |
RegisterLocationChangingHandler |
註冊處理常式來處理傳入導覽事件。 呼叫 NavigateTo 一律會叫用處理常式。 |
GetUriWithQueryParameter | 傳回更新 NavigationManager.Uri 而建構的 URI,並新增、更新或移除單一參數。 如需詳細資訊,請參閱查詢字串一節。 |
member | 描述 |
---|---|
Uri | 取得目前的絕對 URI。 |
BaseUri | 取得基底 URI (具有尾端斜線),其可加在相對 URI 路徑前面以產生絕對 URI。 一般而言,BaseUri 會對應至文件 <base> 元素上的 href 屬性 (<head> 內容的位置)。 |
NavigateTo | 導覽至指定的 URI。 如果 forceLoad 為 true :
replace 為 true ,則會取代瀏覽器歷程記錄中的目前 URI,而不是將新的 URI 推送至歷程記錄堆疊。 |
LocationChanged | 導覽位置變更時引發的事件。 如需詳細資訊,請參閱位置變更一節。 |
ToAbsoluteUri | 將相對 URI 轉換成絕對 URI。 |
ToBaseRelativePath | 根據應用程式的基底 URI,將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。 如需範例,請參閱產生相對於基底 URI 前置詞的 URI 一節。 |
GetUriWithQueryParameter | 傳回更新 NavigationManager.Uri 而建構的 URI,並新增、更新或移除單一參數。 如需詳細資訊,請參閱查詢字串一節。 |
member | 描述 |
---|---|
Uri | 取得目前的絕對 URI。 |
BaseUri | 取得基底 URI (具有尾端斜線),其可加在相對 URI 路徑前面以產生絕對 URI。 一般而言,BaseUri 會對應至文件 <base> 元素上的 href 屬性 (<head> 內容的位置)。 |
NavigateTo | 導覽至指定的 URI。 如果 forceLoad 為 true :
|
LocationChanged | 導覽位置變更時引發的事件。 |
ToAbsoluteUri | 將相對 URI 轉換成絕對 URI。 |
ToBaseRelativePath | 根據應用程式的基底 URI,將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。 如需範例,請參閱產生相對於基底 URI 前置詞的 URI 一節。 |
位置變更
針對 LocationChanged 事件,LocationChangedEventArgs 會提供導覽事件的下列相關資訊:
- Location:新位置的 URL。
- IsNavigationIntercepted:如果為
true
,Blazor 會從瀏覽器攔截導覽。 如果為false
,則 NavigationManager.NavigateTo 會導致導覽發生。
下列 元件:
- 使用 NavigateTo 選取按鈕時,導覽至應用程式的
Counter
元件 (Counter.razor
)。 - 訂閱 NavigationManager.LocationChanged 以處理位置變更事件。
當架構呼叫
Dispose
時,會解除連結HandleLocationChanged
方法。 解除連結該方法會允許為元件進行記憶體回收。選取按鈕時,記錄器實作會記錄下列資訊:
BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:{PORT}/counter
Navigate.razor
:
@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<PageTitle>Navigate</PageTitle>
<h1>Navigate Example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent() => Navigation.NavigateTo("counter");
protected override void OnInitialized() =>
Navigation.LocationChanged += HandleLocationChanged;
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e) =>
Logger.LogInformation("URL of new location: {Location}", e.Location);
public void Dispose() => Navigation.LocationChanged -= HandleLocationChanged;
}
@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<PageTitle>Navigate</PageTitle>
<h1>Navigate Example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent() => Navigation.NavigateTo("counter");
protected override void OnInitialized() =>
Navigation.LocationChanged += HandleLocationChanged;
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e) =>
Logger.LogInformation("URL of new location: {Location}", e.Location);
public void Dispose() => Navigation.LocationChanged -= HandleLocationChanged;
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
如需元件處置的詳細資訊,請參閱 ASP.NET Core Razor 元件生命週期。
增強的瀏覽和表單處理
本章節適用 Blazor Web App。
Blazor Web App Web Apps 有兩種類型的路由,可用於頁面導覽和表單處理要求:
- 一般導覽 (跨文件導覽):針對要求 URL 觸發全頁重新載入。
- 增強式導覽 (相同文件導覽):Blazor 會攔截要求並改為執行
fetch
要求。 然後 Blazor 會將回應內容修補到頁面的 DOM。 Blazor 的增強式導覽和表單處理可避免需要完整頁面重新載入,並可保留更多頁面狀態,因此頁面載入速度較快,通常不會遺失使用者在頁面上的捲動位置。
在以下情況中,增強式導覽可供使用:
- 使用 Blazor Web App 式指令碼 (
blazor.web.js
),而不是 Blazor Server 指令碼 (blazor.server.js
) 或 Blazor WebAssembly 指令碼 (blazor.webassembly.js
)。 - 該功能不會明確停用。
- 目的地 URL 位於內部基底 URI 空間內 (應用程式的基底路徑)。
如果已啟用伺服器端路由和增強式瀏覽,則只會針對從互動式執行階段起始的程式設計導覽來叫用位置變更處理常式。 在未來版本中,其他類型的瀏覽 (例如追蹤連結) 也可能會叫用位置變更處理常式。
當出現增強式瀏覽時,通常會叫用向互動式伺服器和 WebAssembly 執行階段註冊的 LocationChanged
事件處理常式。 在某些情況下,位置變更處理常式可能不會攔截增強式導覽。 例如,使用者可能會在互動式執行階段變得可用之前切換至另一個頁面。 因此,很重要的是應用程式邏輯不依賴叫用位置變更處理常式,因為無法保證處理常式的執行。
呼叫 NavigateTo 時:
- 如果
forceLoad
為false
,則為預設值:- 且增強式導覽可在目前 URL 取得,則 Blazor 的增強式導覽會啟動。
- 否則,Blazor 會針對要求的 URL 執行完整頁面重新載入。
- 如果
forceLoad
為true
:Blazor 會針對要求的 URL 執行完整頁面重新載入,無論增強式導覽是否可供使用。
您可以呼叫 NavigationManager.Refresh(bool forceLoad = false)
來重新整理目前的頁面,其一律會執行增強式導覽 (如果可用)。 如果增強式導覽無法使用,Blazor 會執行完整頁面重新載入。
Navigation.Refresh();
將 true
傳遞至 forceLoad
參數,以確保一律會執行完整頁面重新載入,即使增強式導覽可供使用:
Navigation.Refresh(true);
預設會啟用增強式導覽,但可以使用 data-enhance-nav
HTML 屬性,以階層方式及依每一連結為基礎來控制。
下列範例會停用增強式導覽:
<a href="redirect" data-enhance-nav="false">
GET without enhanced navigation
</a>
<ul data-enhance-nav="false">
<li>
<a href="redirect">GET without enhanced navigation</a>
</li>
<li>
<a href="redirect-2">GET without enhanced navigation</a>
</li>
</ul>
如果目的地為非 Blazor 端點,則不適用增強式導覽,且用戶端 JavaScript 會以完整頁面載入方式重試。 這可確保不會對不應修補至現有頁面的相關外部頁面架構造成混淆。
若要啟用增強式表單處理,請將 Enhance 參數新增至 EditForm 表單或將 data-enhance
屬性新增至 HTML 表單 (<form>
):
<EditForm ... Enhance ...>
...
</EditForm>
<form ... data-enhance ...>
...
</form>
增強式表單處理不是階層式,而且不會流向子表單:
不支援:您無法在表單的上階元素上設定增強式導覽,以啟用表單的增強式導覽。
<div ... data-enhance ...>
<form ...>
<!-- NOT enhanced -->
</form>
</div>
增強式表單貼文僅適用 Blazor 端點。 將增強式表單張貼至非 Blazor 端點會導致錯誤。
若要停用增強式導覽:
- 針對 EditForm,請從表單元素中移除 Enhance 參數 (或將其設定為
false
:Enhance="false"
)。 - 針對 HTML
<form>
,請從表單元素移除data-enhance
屬性 (或將其設定為false
:data-enhance="false"
)。
如果更新的內容不屬於伺服器轉譯的一部分,Blazor 的增強式導覽和表單遞交可能會復原對 DOM 的動態變更。 若要保留元素的內容,請使用 data-permanent
屬性。
在下列範例中,當頁面載入時,指令碼會動態更新 <div>
元素的內容:
<div data-permanent>
...
</div>
在用戶端上啟動 Blazor 之後,您可以使用 enhancedload
事件來接聽增強式頁面更新。 這會允許將變更重新套用至可能已由增強頁面更新復原的 DOM。
Blazor.addEventListener('enhancedload', () => console.log('Enhanced update!'));
若要全域停用增強式導覽和表單處理,請參閱 ASP.NET Core Blazor 啟動。
使用 靜態伺服器端轉譯 (靜態 SSR) 的增強式瀏覽在載入 JavaScript 時需要特別注意。 如需詳細資訊,請參閱 ASP.NET Core Blazor JavaScript 搭配靜態伺服器端轉譯 (靜態 SSR)。
產生相對於基底 URI 前置詞的 URI
根據應用程式的基底 URI,ToBaseRelativePath 會將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。
請考慮下列範例:
try
{
baseRelativePath = Navigation.ToBaseRelativePath(inputURI);
}
catch (ArgumentException ex)
{
...
}
如果應用程式的基底 URI 是 https://localhost:8000
,則會取得下列結果:
- 在
inputURI
中傳入https://localhost:8000/segment
會導致segment
為baseRelativePath
。 - 在
inputURI
中傳入https://localhost:8000/segment1/segment2
會導致segment1/segment2
為baseRelativePath
。
如果應用程式的基底 URI 不符合 inputURI
的基底 URI,則會擲回 ArgumentException。
在 inputURI
中傳入 https://localhost:8001/segment
會產生下列例外狀況:
System.ArgumentException: 'The URI 'https://localhost:8001/segment' is not contained by the base URI 'https://localhost:8000/'.'
導覽歷程記錄狀態
NavigationManager 會使用瀏覽器的歷程記錄 API 來維護與應用程式進行的每個位置變更相關聯的導覽歷程記錄狀態。 維護歷程記錄狀態在外部重新導向案例中特別有用,例如 驗證具有外部 identity 提供者之使用者時。 如需詳細資訊,請參閱導覽選項一節。
導覽選項
將 NavigationOptions 傳遞至 NavigateTo 以控制下列行為:
- ForceLoad:略過用戶端路由,並強制瀏覽器從伺服器載入新頁面,無論 URI 是否由用戶端路由器處理。 預設值是
false
。 - ReplaceHistoryEntry:取代歷程記錄堆疊中的目前項目。 如果為
false
,則將新項目附加至歷程記錄堆疊。 預設值是false
。 - HistoryEntryState:取得或設定要附加至歷程記錄項目的狀態。
Navigation.NavigateTo("/path", new NavigationOptions
{
HistoryEntryState = "Navigation state"
});
如需在處理位置變更時取得與目標歷程記錄項目相關聯狀態的詳細資訊,請參閱處理/防止位置變更一節。
查詢字串
使用 [SupplyParameterFromQuery]
屬性來指定元件參數來自查詢字串。
使用 [SupplyParameterFromQuery]
屬性搭配 [Parameter]
屬性來指定可路由元件的元件參數來自查詢字串。
注意
元件參數只能接收具有 @page
指示詞的可路由元件中的查詢參數值。
只有可路由元件會直接接收查詢參數,以避免破壞由上而下的資訊流程,並讓架構和應用程式的參數處理順序清楚。 此設計可避免撰寫時假設特定參數處理順序的應用程式程式碼中發生細微的錯誤。 您可以自由定義自訂串聯參數,或直接指派給一般元件參數,以便將查詢參數值傳遞至不可路由的元件。
從查詢字串提供的元件參數可支援下列型別:
- 上述型別的可為 Null 的變體。
- 上述型別的陣列,無論是可為 Null 還是不可為 Null。
會針對指定的型別 (CultureInfo.InvariantCulture) 套用正確的不因文化特性而異的格式。
指定 Name 屬性的 [SupplyParameterFromQuery]
屬性,以使用與元件參數名稱不同的查詢參數名稱。 在下列範例中,元件參數的 C# 名稱是 {COMPONENT PARAMETER NAME}
。 為 {QUERY PARAMETER NAME}
預留位置指定了不同的查詢參數名稱:
不同於元件參數屬性 ([Parameter]
),除了 public
之外,[SupplyParameterFromQuery]
屬性還可以標示為 private
。
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
private string? {COMPONENT PARAMETER NAME} { get; set; }
和元件參數屬性 ([Parameter]
) 一樣,[SupplyParameterFromQuery]
屬性在 .NET 6/7 中始終為 public
屬性。 在 .NET 8 或更新版本中,[SupplyParameterFromQuery]
屬性可以標示為 public
或 private
。
[Parameter]
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }
在下列範例中,當 URL 為 /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman
:
Filter
屬性會解析為scifi stars
。Page
屬性會解析為3
。Stars
陣列會從名為star
(Name = "star"
) 的查詢參數填入,並解析為LeVar Burton
和Gary Oldman
。
注意
下列可路由頁面元件中的查詢字串參數也可以在沒有 @page
指示詞的非可路由元件中運作 (例如,用於其他元件的共用 Search
元件的 Search.razor
)。
Search.razor
:
@page "/search"
<h1>Search Example</h1>
<p>Filter: @Filter</p>
<p>Page: @Page</p>
@if (Stars is not null)
{
<p>Stars:</p>
<ul>
@foreach (var name in Stars)
{
<li>@name</li>
}
</ul>
}
@code {
[SupplyParameterFromQuery]
private string? Filter { get; set; }
[SupplyParameterFromQuery]
private int? Page { get; set; }
[SupplyParameterFromQuery(Name = "star")]
private string[]? Stars { get; set; }
}
Search.razor
:
@page "/search"
<h1>Search Example</h1>
<p>Filter: @Filter</p>
<p>Page: @Page</p>
@if (Stars is not null)
{
<p>Stars:</p>
<ul>
@foreach (var name in Stars)
{
<li>@name</li>
}
</ul>
}
@code {
[Parameter]
[SupplyParameterFromQuery]
public string? Filter { get; set; }
[Parameter]
[SupplyParameterFromQuery]
public int? Page { get; set; }
[Parameter]
[SupplyParameterFromQuery(Name = "star")]
public string[]? Stars { get; set; }
}
使用 GetUriWithQueryParameter 來在目前 URL 上新增、變更或移除一或多個查詢參數:
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})
針對上述範例:
{NAME}
預留位置會指定查詢參數名稱。{VALUE}
預留位置會將值指定為支援的型別。 本節稍後會列出支援的型別。- 以單一參數傳回字串,其等於目前的 URL:
- 如果查詢參數名稱不存在於目前 URL 中,則新增。
- 如果查詢參數存在於目前的 URL 中,則更新為所提供的值。
- 如果所提供值的型別可為 Null,且值為
null
,則移除。
- 會針對指定的型別 (CultureInfo.InvariantCulture) 套用正確的不因文化特性而異的格式。
- 查詢參數名稱和值會以 URL 編碼。
- 如果型別有多個執行個體,則會取代具有相符查詢參數名稱的所有值。
呼叫 GetUriWithQueryParameters 以建立自 Uri 建構的 URI,並新增、更新或移除多個參數。 針對每個值,架構會使用 value?.GetType()
來判斷每個查詢參數的執行階段型別,並選取正確的不因文化特性而異格式。 架構會針對不支援的型別擲回錯誤。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameters({PARAMETERS})
{PARAMETERS}
預留位置是 IReadOnlyDictionary<string, object>
。
將 URI 字串傳遞至 GetUriWithQueryParameters,以從提供的 URI 產生新的 URI,並新增、更新或移除多個參數。 針對每個值,架構會使用 value?.GetType()
來判斷每個查詢參數的執行階段型別,並選取正確的不因文化特性而異格式。 架構會針對不支援的型別擲回錯誤。 本節稍後會列出支援的型別。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
{URI}
預留位置是含有或不含查詢字串的 URI。{PARAMETERS}
預留位置是IReadOnlyDictionary<string, object>
。
支援的型別與路由條件約束支援的型別相同:
bool
DateTime
decimal
double
float
Guid
int
long
string
其他支援的型別包括:
- 上述型別的可為 Null 的變體。
- 上述型別的陣列,無論是可為 Null 還是不可為 Null。
警告
透過預設啟用的壓縮,避免建立安全 (經過驗證/授權) 的互動式伺服器端元件來呈現來自不受信任來源的資料。 不受信任的來源包括路由參數、查詢字串、來自 JS 互通性的資料,以及第三方使用者可以控制的任何其他資料來源 (資料庫、外部服務)。 如需詳細資訊,請參閱 ASP.NET Core BlazorSignalR 指引和 ASP.NET Core Blazor 互動式伺服器端轉譯的威脅防護指引。
參數存在時,取代查詢參數值
Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
目前的 URL | 產生的 URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&age=42 |
scheme://host/?full%20name=Morena%20Baccarin&age=42 |
scheme://host/?fUlL%20nAmE=David%20Krumholtz&AgE=42 |
scheme://host/?full%20name=Morena%20Baccarin&AgE=42 |
scheme://host/?full%20name=Jewel%20Staite&age=42&full%20name=Summer%20Glau |
scheme://host/?full%20name=Morena%20Baccarin&age=42&full%20name=Morena%20Baccarin |
scheme://host/?full%20name=&age=42 |
scheme://host/?full%20name=Morena%20Baccarin&age=42 |
scheme://host/?full%20name= |
scheme://host/?full%20name=Morena%20Baccarin |
參數不存在時,附加查詢參數和值
Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
目前的 URL | 產生的 URL |
---|---|
scheme://host/?age=42 |
scheme://host/?age=42&name=Morena%20Baccarin |
scheme://host/ |
scheme://host/?name=Morena%20Baccarin |
scheme://host/? |
scheme://host/?name=Morena%20Baccarin |
參數值為 null
時,移除查詢參數
Navigation.GetUriWithQueryParameter("full name", (string)null)
目前的 URL | 產生的 URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&age=42 |
scheme://host/?age=42 |
scheme://host/?full%20name=Sally%20Smith&age=42&full%20name=Summer%20Glau |
scheme://host/?age=42 |
scheme://host/?full%20name=Sally%20Smith&age=42&FuLl%20NaMe=Summer%20Glau |
scheme://host/?age=42 |
scheme://host/?full%20name=&age=42 |
scheme://host/?age=42 |
scheme://host/?full%20name= |
scheme://host/ |
新增、更新和移除查詢參數
在以下範例中:
- 移除
name
,如果存在。 age
會新增並具有值25
(int
),如果不存在。 如果存在,則將age
更新為25
的值。eye color
會新增或更新為green
的值。
Navigation.GetUriWithQueryParameters(
new Dictionary<string, object?>
{
["name"] = null,
["age"] = (int?)25,
["eye color"] = "green"
})
目前的 URL | 產生的 URL |
---|---|
scheme://host/?name=David%20Krumholtz&age=42 |
scheme://host/?age=25&eye%20color=green |
scheme://host/?NaMe=David%20Krumholtz&AgE=42 |
scheme://host/?age=25&eye%20color=green |
scheme://host/?name=David%20Krumholtz&age=42&keepme=true |
scheme://host/?age=25&keepme=true&eye%20color=green |
scheme://host/?age=42&eye%20color=87 |
scheme://host/?age=25&eye%20color=green |
scheme://host/? |
scheme://host/?age=25&eye%20color=green |
scheme://host/ |
scheme://host/?age=25&eye%20color=green |
支援可列舉的值
在以下範例中:
full name
會新增或更新為Morena Baccarin
,單一值。ping
參數會新增或取代為35
、16
、87
和240
。
Navigation.GetUriWithQueryParameters(
new Dictionary<string, object?>
{
["full name"] = "Morena Baccarin",
["ping"] = new int?[] { 35, 16, null, 87, 240 }
})
目前的 URL | 產生的 URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&ping=8&ping=300 |
scheme://host/?full%20name=Morena%20Baccarin&ping=35&ping=16&ping=87&ping=240 |
scheme://host/?ping=8&full%20name=David%20Krumholtz&ping=300 |
scheme://host/?ping=35&full%20name=Morena%20Baccarin&ping=16&ping=87&ping=240 |
scheme://host/?ping=8&ping=300&ping=50&ping=68&ping=42 |
scheme://host/?ping=35&ping=16&ping=87&ping=240&full%20name=Morena%20Baccarin |
使用新增或修改的查詢字串導覽
若要使用新增或修改的查詢字串導覽,請將產生的 URL 傳遞至 NavigateTo。
下列範例會呼叫:
- GetUriWithQueryParameter 來使用
Morena Baccarin
的值以新增或取代name
查詢參數。 - 呼叫 NavigateTo 以觸發導覽至新 URL。
Navigation.NavigateTo(
Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));
要求的查詢字串是從 NavigationManager.Uri 屬性取得:
@inject NavigationManager Navigation
...
var query = new Uri(Navigation.Uri).Query;
若要剖析查詢字串的參數,其中一種方法是使用 URLSearchParams
搭配 JavaScript (JS) Interop:
export createQueryString = (string queryString) => new URLSearchParams(queryString);
如需 JavaScript 隔離與 JavaScript 模組的詳細資訊,請參閱在 ASP.NET Core Blazor 中從 .NET 方法呼叫 JavaScript 函式。
雜湊路由至具名元素
使用下列方法並搭配元素的雜湊 (#
) 參考來導覽至具名元素。 路由至元件內的元素,並使用根相對路徑路由至外部元件中的元素。 前置正斜線 (/
) 是選用的。
下列每個方法的範例會示範如何導覽至 Counter
元件中具有 targetElement
的 id
的元素:
具有
href
的錨點元素 (<a>
):<a href="/counter#targetElement">
具有
href
的 NavLink 元件:<NavLink href="/counter#targetElement">
NavigationManager.NavigateTo 會傳遞相對 URL:
Navigation.NavigateTo("/counter#targetElement");
下列範例示範將雜湊路由至元件內的具名 H2 標題和至外部元件。
在 Home
(Home.razor
) 和 Counter
(Counter.razor
) 元件中,將下列標記放置在現有元件標記的底部,以做為導覽目標。 <div>
會建立人工垂直空間來示範瀏覽器捲動行為:
<div class="border border-info rounded bg-info" style="height:500px"></div>
<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
將下列 HashedRouting
元件新增至應用程式。
HashedRouting.razor
:
@page "/hashed-routing"
@inject NavigationManager Navigation
<PageTitle>Hashed routing</PageTitle>
<h1>Hashed routing to named elements</h1>
<ul>
<li>
<a href="/hashed-routing#targetElement">
Anchor in this component
</a>
</li>
<li>
<a href="/#targetElement">
Anchor to the <code>Home</code> component
</a>
</li>
<li>
<a href="/counter#targetElement">
Anchor to the <code>Counter</code> component
</a>
</li>
<li>
<NavLink href="/hashed-routing#targetElement">
Use a `NavLink` component in this component
</NavLink>
</li>
<li>
<button @onclick="NavigateToElement">
Navigate with <code>NavigationManager</code> to the
<code>Counter</code> component
</button>
</li>
</ul>
<div class="border border-info rounded bg-info" style="height:500px"></div>
<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
@code {
private void NavigateToElement()
{
Navigation.NavigateTo("/counter#targetElement");
}
}
使用 <Navigating>
內容的使用者互動
如果在瀏覽期間發生重大延遲,例如Blazor WebAssembly應用程式中延遲載入組件或 Blazor 伺服器端應用程式的網路連線速度緩慢時,Router 元件可以向使用者指出目前正在進行頁面轉換。
在指定 Router 元件的元件頂端,新增 Microsoft.AspNetCore.Components.Routing 命名空間的 @using
指示詞:
@using Microsoft.AspNetCore.Components.Routing
提供內容給 Navigating 參數,以在頁面轉換事件期間顯示。
在路由器元素內容中 (<Router>...</Router>
):
<Navigating>
<p>Loading the requested page…</p>
</Navigating>
如需使用 Navigating 屬性的範例,請參閱 ASP.NET Core Blazor WebAssembly 中的延遲載入組件。
使用 OnNavigateAsync
處理非同步導覽事件
Router 元件支援 OnNavigateAsync 功能。 當使用者執行下列動作時會叫用 OnNavigateAsync 處理常式:
- 第一次瀏覽路由,方法是直接在其瀏覽器中導覽至路由。
- 使用連結或 NavigationManager.NavigateTo 叫用導覽至新的路由。
<Router AppAssembly="typeof(App).Assembly"
OnNavigateAsync="OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
...
}
}
<Router AppAssembly="typeof(Program).Assembly"
OnNavigateAsync="OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
...
}
}
如需使用 OnNavigateAsync 的範例,請參閱 ASP.NET Core Blazor WebAssembly 中的延遲載入組件。
在伺服器上預先轉譯時,OnNavigateAsync 會執行兩次:
- 一次是在要求的端點元件最初靜態轉譯時。
- 第二次是在瀏覽器轉譯端點元件時。
若要防止 OnNavigateAsync 中的開發人員程式碼執行兩次, Routes
元件可以儲存 NavigationContext 以便在 OnAfterRender{Async}
生命週期中使用,其中可以檢查 firstRender
。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先轉譯一節。
若要防止 OnNavigateAsync 中的開發人員程式碼執行兩次,App
元件可以將 NavigationContext 儲存在 OnAfterRender{Async}
中供使用,在其中可以檢查 firstRender
。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先轉譯一節。
在 OnNavigateAsync
中處理取消
傳遞至 OnNavigateAsync 回撥的 NavigationContext 物件會包含發生新導覽事件時設定的 CancellationToken。 設定此取消權杖時,必須擲回 OnNavigateAsync 回撥,以避免在過期的導覽上繼續執行 OnNavigateAsync 回撥。
如果使用者導覽至某個端點,但會立即導覽至新的端點,則應用程式不應該繼續執行第一個端點的 OnNavigateAsync 回撥。
在以下範例中:
- 取消權杖在對
PostAsJsonAsync
的呼叫中傳遞,其可以在使用者導覽離開/about
端點時取消 POST。 - 取消權杖在產品預先擷取作業期間使用者導覽離開
/store
端點時設定。
@inject HttpClient Http
@inject ProductCatalog Products
<Router AppAssembly="typeof(App).Assembly"
OnNavigateAsync="OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext context)
{
if (context.Path == "/about")
{
var stats = new Stats { Page = "/about" };
await Http.PostAsJsonAsync("api/visited", stats,
context.CancellationToken);
}
else if (context.Path == "/store")
{
var productIds = new[] { 345, 789, 135, 689 };
foreach (var productId in productIds)
{
context.CancellationToken.ThrowIfCancellationRequested();
Products.Prefetch(productId);
}
}
}
}
@inject HttpClient Http
@inject ProductCatalog Products
<Router AppAssembly="typeof(Program).Assembly"
OnNavigateAsync="OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext context)
{
if (context.Path == "/about")
{
var stats = new Stats { Page = "/about" };
await Http.PostAsJsonAsync("api/visited", stats,
context.CancellationToken);
}
else if (context.Path == "/store")
{
var productIds = new[] { 345, 789, 135, 689 };
foreach (var productId in productIds)
{
context.CancellationToken.ThrowIfCancellationRequested();
Products.Prefetch(productId);
}
}
}
}
注意
如果取消 NavigationContext 中的取消權杖但未擲回,可能會導致非預期的行為,例如轉譯先前導覽的元件。
處理/防止位置變更
RegisterLocationChangingHandler 會註冊處理常式來處理傳入導覽事件。 LocationChangingContext 提供的處理常式內容包含下列屬性:
- TargetLocation:取得目標位置。
- HistoryEntryState:取得與目標歷程記錄項目相關聯的狀態。
- IsNavigationIntercepted:取得導覽是否從連結攔截。
- CancellationToken:取得 CancellationToken,以判斷導覽是否已取消,例如,以判斷使用者是否觸發了不同的導覽。
- PreventNavigation:呼叫以防止導覽繼續。
元件可以用 OnAfterRender{Async}
生命週期法註冊多個位置變更處理常式。 導覽會叫用跨整個應用程式 (跨多個元件) 註冊的所有位置變更處理常式,以及任何內部導覽會並行執行它們。 除了叫用 NavigateTo 處理常式之外:
- 選取內部連結時,這是指向應用程式基底路徑下 URL 的連結。
- 在瀏覽器中使用向前和返回按鈕導覽時。
處理常式只會針對應用程式內的內部導覽執行。 如果使用者選取導覽至不同網站的連結,或手動將網址列變更為不同的網站,則不會執行位置變更處理常式。
實作 IDisposable 並處置已註冊的處理常式,以取消註冊它們。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件生命週期。
重要
處理位置變更時,請勿嘗試透過 JavaScript (JS) Interop 執行 DOM 清除工作。 在用戶端的 JS 中使用 MutationObserver
模式 。 如需詳細資訊,請參閱 ASP.NET Core Blazor JavaScript 互通性 (JS Interop)。
在下列範例中,會註冊位置變更處理常式來導覽事件。
NavHandler.razor
:
@page "/nav-handler"
@implements IDisposable
@inject NavigationManager Navigation
<p>
<button @onclick="@(() => Navigation.NavigateTo("/"))">
Home (Allowed)
</button>
<button @onclick="@(() => Navigation.NavigateTo("/counter"))">
Counter (Prevented)
</button>
</p>
@code {
private IDisposable? registration;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
registration =
Navigation.RegisterLocationChangingHandler(OnLocationChanging);
}
}
private ValueTask OnLocationChanging(LocationChangingContext context)
{
if (context.TargetLocation == "/counter")
{
context.PreventNavigation();
}
return ValueTask.CompletedTask;
}
public void Dispose() => registration?.Dispose();
}
由於可以非同步取消內部導覽,因此可能會發生對已註冊的處理常式進行多個重疊呼叫。 例如,當使用者快速選取頁面上的返回按鈕,或在執行導覽之前選取多個連結時,可能會發生多個處理常式呼叫。 以下是非同步導覽邏輯的摘要:
- 如果已註冊任何位置變更處理常式,則所有導覽一開始都會還原,然後在導覽未取消時重新執行。
- 如果提出重疊的導覽要求,最新的要求一律會取消先前的要求,這表示以下:
- 應用程式可能會將多個返回和向前按鈕選取項目視為單一選取項目。
- 如果使用者在導覽完成之前選取多個連結,選取的最後一個連結會決定導覽。
如需傳遞 NavigationOptions 至 NavigateTo 以控制導覽歷程記錄堆疊的項目和狀態的詳細資訊,請參閱導覽選項一節。
如需其他範例程式碼,請參閱 BasicTestApp
中的 NavigationManagerComponent
(dotnet/aspnetcore
參考來源)。
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
只要經過轉譯,NavigationLock
元件就會攔截導覽事件,在決定繼續或取消之前,有效地「鎖定」任何指定的導覽。 當導覽攔截的範圍可設定為元件的存留期時使用 NavigationLock
。
NavigationLock 參數:
- ConfirmExternalNavigation 會設定瀏覽器對話方塊,以提示使用者確認或取消外部導覽。 預設值是
false
。 顯示確認對話方塊需要與頁面進行初始使用者互動,才能觸發使用瀏覽器網址列中 URL 的外部導覽。 如需互動需求的詳細資訊,請參閱 Window:beforeunload
事件 (MDN 文件)。 - OnBeforeInternalNavigation 會設定內部導覽事件的回撥。
在下列 NavLock
元件中:
- 使用者必須先確認嘗試遵循 Microsoft 網站的連結,導覽至
https://www.microsoft.com
才能成功。 - 如果使用者拒絕透過產生 JS
confirm
對話方塊的 JavaScript (JS) Interop 呼叫確認導覽,則會呼叫 PreventNavigation 以防止導覽發生。
NavLock.razor
:
@page "/nav-lock"
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation
<NavigationLock ConfirmExternalNavigation="true"
OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
<p>
<button @onclick="Navigate">Navigate</button>
</p>
<p>
<a href="https://www.microsoft.com">Microsoft homepage</a>
</p>
@code {
private void Navigate()
{
Navigation.NavigateTo("/");
}
private async Task OnBeforeInternalNavigation(LocationChangingContext context)
{
var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm",
"Are you sure you want to navigate to the root page?");
if (!isConfirmed)
{
context.PreventNavigation();
}
}
}
如需其他範例程式碼,請參閱 BasicTestApp
中的 ConfigurableNavigationLock
元件 (dotnet/aspnetcore
參考來源)。
NavLink
元件
建立導覽連結時,請使用 NavLink 元件以取代 HTML 超連結元素 (<a>
)。 NavLink 元件的行為與 <a>
元素類似,不同之處在於它會根據其 href
是否符合目前的 URL 來切換 active
CSS 類別。 active
類別可協助使用者了解哪個頁面是顯示的導覽連結中的作用中頁面。 選擇性地將 CSS 類別名稱指派給 NavLink.ActiveClass,以在目前的路由符合 href
時,將自訂 CSS 類別套用至轉譯的連結。
您可以指派給 <NavLink>
元素的 Match
屬性有兩個 NavLinkMatch 選項:
- NavLinkMatch.All:當 NavLink 符合整個目前的 URL 時,它會作用中。
- NavLinkMatch.Prefix (預設值):當 NavLink 符合目前 URL 的任何前置詞時,它會作用中。
在上述範例中, HomeNavLinkhref=""
符合 home URL,且只會在應用程式的預設基底路徑 (/
) 接收 active
CSS 類別。 當使用者瀏覽具有 component
前置詞的任何 URL 時 (例如 /component
和 /component/another-segment
),第二個 NavLink 會接收 active
類別。
其他 NavLink 元件屬性會傳遞至轉譯的錨點標記。 在下列範例中,NavLink 元件包含 target
屬性:
<NavLink href="example-page" target="_blank">Example page</NavLink>
會轉譯下列 HTML 標記:
<a href="example-page" target="_blank">Example page</a>
警告
由於 Blazor 轉譯子內容的方式,如果在 NavLink
(子) 元件的內容中使用遞增迴圈變數,則在 for
迴圈內轉譯 NavLink
元件需要用到本機索引變數:
@for (int c = 1; c < 4; c++)
{
var ct = c;
<li ...>
<NavLink ...>
<span ...></span> Product #@ct
</NavLink>
</li>
}
在此案例中使用索引變數是在其子內容中使用迴圈變數的任何子元件的需求,而不只是 NavLink
元件。
或者,使用 foreach
迴圈搭配 Enumerable.Range:
@foreach (var c in Enumerable.Range(1, 3))
{
<li ...>
<NavLink ...>
<span ...></span> Product #@c
</NavLink>
</li>
}
NavLink 元件輸入可以透過反映從應用程式的元件動態建立。 下列範例示範進一步自訂的一般方法。
針對下列示範,應用程式元件會使用一致的標準命名慣例:
- 可路由元件檔案名稱會使用大駝峰式命名法†,例如
Pages/ProductDetail.razor
。 - 可路由元件檔案路徑會與其使用烤肉串式命名法‡ 的 URL 相符,元件的路由範本中的單字之間會出現連字號。 例如,針對路由範本為
/product-detail
(@page "/product-detail"
) 的ProductDetail
元件,我們會透過瀏覽器在相對 URL/product-detail
中提出要求。
†Pascal 命名法 (大駝峰式命名法) 是不含空格和標點符號的命名慣例,且每個單字的第一個字母會大寫,包括第一個單字。
‡烤肉串式大小寫是不含空格和標點符號的命名慣例,並且在字組之間會使用小寫字母和虛線。
在預設 Home
頁面底下 NavMenu
元件 (NavMenu.razor
) 的 Razor 標記中,會從集合中新增 NavLink 元件:
<div class="nav-scrollable"
onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu"
aria-hidden="true"></span> Home
</NavLink>
</div>
+ @foreach (var name in GetRoutableComponents())
+ {
+ <div class="nav-item px-3">
+ <NavLink class="nav-link"
+ href="@Regex.Replace(name, @"(\B[A-Z]|\d+)", "-$1").ToLower()">
+ @Regex.Replace(name, @"(\B[A-Z]|\d+)", " $1")
+ </NavLink>
+ </div>
+ }
</nav>
</div>
@code
區塊中的 GetRoutableComponents
方法:
public IEnumerable<string> GetRoutableComponents() =>
Assembly.GetExecutingAssembly()
.ExportedTypes
.Where(t => t.IsSubclassOf(typeof(ComponentBase)))
.Where(c => c.GetCustomAttributes(inherit: true)
.OfType<RouteAttribute>()
.Any())
.Where(c => c.Name != "Home" && c.Name != "Error")
.OrderBy(o => o.Name)
.Select(c => c.Name);
上述範例不會在轉譯的元件清單中包含下列頁面:
Home
頁面: 頁面會與自動產生的連結分開列出,因為它應該出現在清單頂端,並設定Match
參數。Error
頁面: 錯誤頁面只會由架構瀏覽,不應該列出。
有關可在本機執行的樣本應用程式中的上述程式碼範例,請取得 Blazor Web App 或 Blazor WebAssembly 範例應用程式。
ASP.NET Core 端點路由整合
本章節適用於在電路運作的 Blazor Web App。
本節適用 Blazor Server 應用程式。
Blazor Web App 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式已經過設定,會接受與 Program
檔案中的 MapRazorComponents 互動式元件的連入連線。 預設根元件 (第一個載入的元件) 為 App
元件 (App.razor
):
app.MapRazorComponents<App>();
Blazor Server 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式已經過設定,會接受與 Program
檔案中的 MapBlazorHub 互動式元件的連入連線:
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
Blazor Server 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式已經過設定,會接受與 Startup.Configure
中的 MapBlazorHub 互動式元件的連入連線。
一般設定是將所有要求路由至 Razor 頁面,其會做為 Blazor Server 應用程式伺服器端部分的主機。 依照慣例,主機頁面通常會在應用程式的 Pages
資料夾中名為 _Host.cshtml
。
主機檔案中指定的路由稱為後援路由,因為它在路由比對會以低優先順序運作。 當其他路由不符時,會使用後援路由。 這可讓應用程式使用其他控制器和頁面,而不會干擾 Blazor Server 應用程式中的元件路由。
如需針對非根 URL 伺服器裝載設定 MapFallbackToPage 的相關資訊,請參閱裝載和部署 ASP.NET Core Blazor。