ASP.NET 核心 Blazor 路由和導覽

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

本文說明如何管理 Blazor 應用程式要求路由,以及如何使用 NavLink 元件來建立導覽連結。

重要

本文中的程式碼範例會顯示在 Navigation 上呼叫的方法,其為在類別和元件中插入的 NavigationManager

靜態與互動式路由

本節適用於 Blazor Web Apps。

如果 未停用預先轉譯,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) 中。

Router 元件可讓您路由至 Razor 元件。 Router 元件會用於 App 元件 (App.razor)。

編譯具有 @page 指示詞的 Razor 元件 (.razor) 時,會為產生的元件類別提供一個 RouteAttribute,指定元件的路由範本。

當應用程式啟動時,會掃描指定為路由器 AppAssembly 的組件,以收集具有 RouteAttribute 的應用程式元件的路由資訊。

在執行階段,RouteView 元件:

  • Router 接收 RouteData,以及任何路由參數。
  • 使用其配置轉譯指定的元件,包括任何進一步的巢狀配置。

選擇性地為未使用 @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"

<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

Router 不會與查詢字串值互動。 若要使用查詢字串,請參閱查詢字串小節。

除了使用 @page 指示詞將路由範本指定為字串常值,還可以使用 @attribute 指示詞來指定以常數為基礎的路由範本。

在下列範例中,元件中的 @page 指示詞會取代為 @attribute 指示詞,而 Constants.CounterRoute 中的常數型路由範本,其會在應用程式中的其他位置設定為 "/counter":

- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]

注意

隨著 ASP.NET Core 5.0.1 的發行以及在任何其他 5.x 版中,Router 元件會包含設定為 @truePreferExactMatches 參數。 如需詳細資訊,請參閱從 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 Apps 不會使用 NotFound 參數 (<NotFound>...</NotFound> 標記),但為了回溯相容性會支援該參數,以避免架構發生重大變更。 伺服器端 ASP.NET Core 中介軟體管線會處理伺服器上的要求。 使用伺服器端技術來處理不正確的要求。 如需詳細資訊,請參閱 ASP.NET Core Blazor 轉譯模式

從多個組件路由至元件

本節適用於 Blazor Web Apps。

使用 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}"

<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?}"

<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 時,如果使用者在相同的元件內瀏覽,則不會發生 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; }
}

注意

路由條件約束不適用查詢字串值。 若要使用查詢字串,請參閱查詢字串小節。

下表所示的路由條件約束可供使用。 如需會比對不因文化特性而異的路由條件約束,請參閱下表中的警告以取得詳細資訊。

條件約束 範例 範例相符項目 非變異值
culture
比對
bool {active:bool} trueFALSE No
datetime {dob:datetime} 2016-12-312016-12-31 7:32pm Yes
decimal {price:decimal} 49.99-1,000.01 Yes
double {weight:double} 1.234-1,001.01e8 Yes
float {weight:float} 1.234-1,001.01e8 Yes
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638{CD2C1638-1638-72D5-1638-DEADBEEF1638} No
int {id:int} 123456789-123456789 Yes
long {ticks:long} 123456789-123456789 Yes

警告

確認 URL 可以轉換成 CLR 類型的路由條件約束 (例如 intDateTime) 一律使用不因國別而異的文化特性。 這些條件約束假設 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; }
}

針對包含點的 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}"

@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/testPageRoute 的值會設定為 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。 如果 forceLoadfalse
  • 且增強式導覽可在目前 URL 取得,則 Blazor 的增強式導覽會啟動。
  • 否則,Blazor 會針對要求的 URL 執行完整頁面重新載入。
如果 forceLoadtrue
  • 會略過用戶端路由。
  • 無論 URI 是否正常由用戶端互動式路由器處理,都會強制瀏覽器從伺服器載入新頁面。

如需詳細資訊,請參閱增強式導覽和表單處理一節。

如果 replacetrue,則會取代瀏覽器歷程記錄中的目前 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。 如果 forceLoadtrue
  • 會略過用戶端路由。
  • 無論 URI 是否正常由用戶端路由器處理,都會強制瀏覽器從伺服器載入新頁面。
如果 replacetrue,則會取代瀏覽器歷程記錄中的目前 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。 如果 forceLoadtrue
  • 會略過用戶端路由。
  • 無論 URI 是否正常由用戶端路由器處理,都會強制瀏覽器從伺服器載入新頁面。
如果 replacetrue,則會取代瀏覽器歷程記錄中的目前 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。 如果 forceLoadtrue
  • 會略過用戶端路由。
  • 無論 URI 是否正常由用戶端路由器處理,都會強制瀏覽器從伺服器載入新頁面。
LocationChanged 導覽位置變更時引發的事件。
ToAbsoluteUri 將相對 URI 轉換成絕對 URI。
ToBaseRelativePath 根據應用程式的基底 URI,將絕對 URI 轉換成相對於基底 URI 前置詞的 URI。 如需範例,請參閱產生相對於基底 URI 前置詞的 URI 一節。

位置變更

針對 LocationChanged 事件,LocationChangedEventArgs 會提供導覽事件的下列相關資訊:

下列 元件:

  • 使用 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"
@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 Apps。

Blazor Web Apps 有兩種類型的路由,可用於頁面導覽和表單處理要求:

  • 一般導覽 (跨文件導覽):針對要求 URL 觸發全頁重新載入。
  • 增強式導覽 (相同文件導覽)†:Blazor 會攔截要求並改為執行 fetch 要求。 然後 Blazor 會將回應內容修補到頁面的 DOM。 Blazor 的增強式導覽和表單處理可避免需要完整頁面重新載入,並可保留更多頁面狀態,因此頁面載入速度較快,通常不會遺失使用者在頁面上的捲動位置。

†在以下情況中,增強式導覽可供使用:

  • 使用了 Blazor Web 應用程式指令碼 (blazor.web.js),而不是 Blazor Server 指令碼 (blazor.server.js) 或 Blazor WebAssembly 指令碼 (blazor.webassembly.js)。
  • 該功能不會明確停用
  • 目的地 URL 位於內部基底 URI 空間內 (應用程式的基底路徑)。

如果已啟用伺服器端路由和增強式瀏覽,則只會針對從互動式執行階段起始的程式設計導覽來叫用位置變更處理常式。 在未來版本中,其他類型的瀏覽 (例如追蹤連結) 也可能會叫用位置變更處理常式。

當出現增強式瀏覽時,通常會叫用向互動式伺服器和 WebAssembly 執行階段註冊的 LocationChanged事件處理常式。 在某些情況下,位置變更處理常式可能不會攔截增強式導覽。 例如,使用者可能會在互動式執行階段變得可用之前切換至另一個頁面。 因此,很重要的是應用程式邏輯不依賴叫用位置變更處理常式,因為無法保證處理常式的執行。

呼叫 NavigateTo 時:

  • 如果 forceLoadfalse,則為預設值:
    • 且增強式導覽可在目前 URL 取得,則 Blazor 的增強式導覽會啟動。
    • 否則,Blazor 會針對要求的 URL 執行完整頁面重新載入。
  • 如果 forceLoadtrue: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 參數 (或將其設定為 falseEnhance="false")。
  • 針對 HTML <form>,請從表單元素移除 data-enhance 屬性 (或將其設定為 falsedata-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 會導致 segmentbaseRelativePath
  • inputURI 中傳入 https://localhost:8000/segment1/segment2 會導致 segment1/segment2baseRelativePath

如果應用程式的基底 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 來維護與應用程式進行的每個位置變更相關聯的導覽歷程記錄狀態。 維護歷程記錄狀態在外部重新導向案例中特別有用,例如向外部識別提供者驗證使用者時。 如需詳細資訊,請參閱導覽選項一節。

NavigationOptions 傳遞至 NavigateTo 以控制下列行為:

  • ForceLoad:略過用戶端路由,並強制瀏覽器從伺服器載入新頁面,無論 URI 是否由用戶端路由器處理。 預設值是 false
  • ReplaceHistoryEntry:取代歷程記錄堆疊中的目前項目。 如果為 false,則將新項目附加至歷程記錄堆疊。 預設值是 false
  • HistoryEntryState:取得或設定要附加至歷程記錄項目的狀態。
Navigation.NavigateTo("/path", new NavigationOptions
{
    HistoryEntryState = "Navigation state"
});

如需在處理位置變更時取得與目標歷程記錄項目相關聯狀態的詳細資訊,請參閱處理/防止位置變更一節。

查詢字串

使用 [SupplyParameterFromQuery] 屬性來指定元件參數來自查詢字串。

使用 [SupplyParameterFromQuery] 屬性搭配 [Parameter] 屬性來指定可路由元件的元件參數來自查詢字串。

注意

元件參數只能接收具有 @page 指示詞的可路由元件中的查詢參數值。

只有可路由元件會直接接收查詢參數,以避免破壞由上而下的資訊流程,並讓架構和應用程式的參數處理順序清楚。 此設計可避免撰寫時假設特定參數處理順序的應用程式程式碼中發生細微的錯誤。 您可以自由定義自訂串聯參數,或直接指派給一般元件參數,以便將查詢參數值傳遞至不可路由的元件。

從查詢字串提供的元件參數可支援下列型別:

  • boolDateTimedecimaldoublefloatGuidintlongstring
  • 上述型別的可為 Null 的變體。
  • 上述型別的陣列,無論是可為 Null 還是不可為 Null。

會針對指定的型別 (CultureInfo.InvariantCulture) 套用正確的不因文化特性而異的格式。

指定 Name 屬性的 [SupplyParameterFromQuery] 屬性,以使用與元件參數名稱不同的查詢參數名稱。 在下列範例中,元件參數的 C# 名稱是 {COMPONENT PARAMETER NAME}。 為 {QUERY PARAMETER NAME} 預留位置指定了不同的查詢參數名稱:

[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }
[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 BurtonGary 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]
    public string? Filter { get; set; }

    [SupplyParameterFromQuery]
    public int? Page { get; set; }

    [SupplyParameterFromQuery(Name = "star")]
    public 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; }
}

使用 NavigationManager.GetUriWithQueryParameter 來在目前 URL 上新增、變更或移除一或多個查詢參數:

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})

針對上述範例:

  • {NAME} 預留位置會指定查詢參數名稱。 {VALUE} 預留位置會將值指定為支援的型別。 本節稍後會列出支援的型別。
  • 以單一參數傳回字串,其等於目前的 URL:
    • 如果查詢參數名稱不存在於目前 URL 中,則新增。
    • 如果查詢參數存在於目前的 URL 中,則更新為所提供的值。
    • 如果所提供值的型別可為 Null,且值為 null,則移除。
  • 會針對指定的型別 (CultureInfo.InvariantCulture) 套用正確的不因文化特性而異的格式。
  • 查詢參數名稱和值會以 URL 編碼。
  • 如果型別有多個執行個體,則會取代具有相符查詢參數名稱的所有值。

呼叫 NavigationManager.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 參數會新增或取代為 351687240
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

下列範例會呼叫:

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 元件中具有 targetElementid 的元素:

  • 具有 href 的錨點元素 (<a>):

    <a href="/counter#targetElement">
    
  • 具有 hrefNavLink 元件:

    <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&hellip;</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。 如需詳細資訊,請參閱 Blazor 生命週期一文中的使用 JavaScript Interop 預先轉譯

若要防止 OnNavigateAsync 中的開發人員程式碼執行兩次,App 元件可以將 NavigationContext 儲存在 OnAfterRender{Async} 中供使用,在其中可以檢查 firstRender。 如需詳細資訊,請參閱 Blazor 生命週期一文中的使用 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 提供的處理常式內容包含下列屬性:

元件可以在其 OnAfterRenderOnAfterRenderAsync 方法中註冊多個位置變更處理常式。 導覽會叫用跨整個應用程式 (跨多個元件) 註冊的所有位置變更處理常式,以及任何內部導覽會並行執行它們。 除了叫用 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();
}

由於可以非同步取消內部導覽,因此可能會發生對已註冊的處理常式進行多個重疊呼叫。 例如,當使用者快速選取頁面上的返回按鈕,或在執行導覽之前選取多個連結時,可能會發生多個處理常式呼叫。 以下是非同步導覽邏輯的摘要:

  • 如果已註冊任何位置變更處理常式,則所有導覽一開始都會還原,然後在導覽未取消時重新執行。
  • 如果提出重疊的導覽要求,最新的要求一律會取消先前的要求,這表示以下:
    • 應用程式可能會將多個返回和向前按鈕選取項目視為單一選取項目。
    • 如果使用者在導覽完成之前選取多個連結,選取的最後一個連結會決定導覽。

如需傳遞 NavigationOptionsNavigateTo 以控制導覽歷程記錄堆疊的項目和狀態的詳細資訊,請參閱導覽選項一節。

如需其他範例程式碼,請參閱 BasicTestApp 中的 NavigationManagerComponent (dotnet/aspnetcore 參考來源)

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

只要經過轉譯,NavigationLock 元件就會攔截導覽事件,在決定繼續或取消之前,有效地「鎖定」任何指定的導覽。 當導覽攔截的範圍可設定為元件的存留期時使用 NavigationLock

NavigationLock 參數:

在下列 NavLock 元件中:

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 元件以取代 HTML 超連結元素 (<a>)。 NavLink 元件的行為與 <a> 元素類似,不同之處在於它會根據其 href 是否符合目前的 URL 來切換 active CSS 類別。 active 類別可協助使用者了解哪個頁面是顯示的導覽連結中的作用中頁面。 選擇性地將 CSS 類別名稱指派給 NavLink.ActiveClass,以在目前的路由符合 href 時,將自訂 CSS 類別套用至轉譯的連結。

您可以指派給 <NavLink> 元素的 Match 屬性有兩個 NavLinkMatch 選項:

在上述範例中,HomeNavLinkhref="" 符合首頁 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 = 0; c < 10; c++)
{
    var current = c;
    <li ...>
        <NavLink ... href="product-number/@c">
            <span ...></span> Product #@current
        </NavLink>
    </li>
}

在此案例中使用索引變數是在其子內容中使用迴圈變數的任何子元件的需求,而不只是 NavLink 元件。

或者,使用 foreach 迴圈搭配 Enumerable.Range

@foreach (var c in Enumerable.Range(0,10))
{
    <li ...>
        <NavLink ... href="product-number/@c">
            <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()
{
    return 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 頁面: 錯誤頁面只會由架構瀏覽,不應該列出。

有關可在本機運行的範例應用程式中的上述程式碼的範例,請取得 BlazorWeb 應用程式Blazor WebAssembly範例應用程式

ASP.NET Core 端點路由整合

本節適用透過線路運作的 Blazor Web Apps。

本節適用 Blazor Server Web Apps。

Blazor Web 應用程式已整合到 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