Routing

与标准ASP.NETMVC一样,Blazor路由是一种用于检查浏览器的URL并将其与要呈现的页面进行匹配的技术。
image.png
路由比简单地将URL与页面匹配要灵活得多。它允许我们基于文本模式进行匹配,例如,上图中的两个URL将映射到相同的组件,并传入上下文的ID(在本例中为1或4)。

Simulated navigation 模拟导航

当Blazor应用程序在同一个应用程序中导航到新的URL时,它实际上并不是在传统的WWW意义上导航。不向服务器发送请求新页面的内容的请求。相反,Blazor会重写浏览器的URL,然后呈现相关内容。
另请注意,当导航到解析为与当前页面相同类型的组件的新URL时,在导航之前不会销毁该组件,也不会执行OnInitialized*生命周期方法。导航被简单地视为对组件参数的更改。

Defining routes

要定义路由,我们只需在任何组件的顶部添加@page声明。

  1. @page "/"
  2. <h1>Hello, world!</h1>
  3. Welcome to your new app.

如果我们打开为此视图生成的源代码(在obj\Debug\netcoreapp3.0\Razor\Pages\Index.razor.g.cs)中,我们看到@page指令编译为以下代码。

  1. [Microsoft.AspNetCore.Components.LayoutAttribute(typeof(MainLayout))]
  2. [Microsoft.AspNetCore.Components.RouteAttribute("/")]
  3. public class Index : Microsoft.AspNetCore.Components.ComponentBase
  4. {
  5. }

@page指令在组件的类上生成RouteAttribute。在启动期间,Blazor扫描使用RouteAttribute装饰的类,并相应地构建其路由定义。

Route discovery

路由发现由Blazor在其默认项目模板中自动执行。如果我们查看App.razor文件,我们将看到路由器组件的使用。

  1. other code
  2. <Router AppAssembly="typeof(Startup).Assembly">
  3. other code
  4. </Router>
  5. other code

Router组件扫描指定程序集中实现**IComponent**的所有类,然后在类上反映,看看是否用任何**RouteAttribute**属性修饰了它。对于它找到的每个RouteAttribute,它都会解析其URL模板字符串,并将URL到组件的关系添加到其内部路由表中。
这意味着单个组件可以用零个、一个或多个RouteAttribute属性(@page声明)进行修饰。零的组件不能通过URL访问,而多个的组件可以通过它指定的任何URL模板访问。

  1. @page "/"
  2. @page "/greeting"
  3. @page "/HelloWorld"
  4. @page "/hello-world"
  5. <h1>Hello, world!</h1>

页面也可以在组件库中定义。

Route parameters

到目前为止,我们已经了解了如何将静态网址链接到Blazor组件。静态URL只对静态内容有用,如果我们希望同一组件根据URL中的信息(如客户ID)呈现不同的视图,则需要使用路由参数。
添加组件的@page声明时,通过将路由参数的名称用一对{大括号}括起来,可以在URL中定义路由参数。

  1. @page "/customer/{CustomerId}

Capturing a parameter value

捕获参数值就像添加同名属性并用[Parameter]属性修饰它一样简单。

  1. @page "/"
  2. @page "/customer/{CustomerId}"
  3. <h1>
  4. Customer:
  5. @if (string.IsNullOrEmpty(CustomerId))
  6. {
  7. @:None
  8. }
  9. else
  10. {
  11. @CustomerId
  12. }
  13. </h1>
  14. <h3>Select a customer</h3>
  15. <ul>
  16. <li><a href="/customer/Microsoft">Microsoft</a></li>
  17. <li><a href="/customer/Google">Google</a></li>
  18. <li><a href="/customer/IBM">IBM</a></li>
  19. </ul>
  20. @code {
  21. [Parameter]
  22. public string CustomerId { get; set; }
  23. }

请注意,当导航到解析为与当前页面相同类型的组件的新URL时,在导航之前不会销毁该组件,也不会执行OnInitialized*生命周期方法。导航被简单地视为对组件参数的更改。

Constraining route parameters 约束路由参数

除了能够指定包含参数的URL模板外,还可以确保Blazor仅在参数值满足特定条件时才将URL与组件匹配。
例如,在采购订单编号始终为整数的应用程序中,只有当OrderNumber的URL值实际上是数字时,我们才希望URL中的参数与用于显示采购订单的组件匹配。
要定义参数的约束,它的后缀是冒号,然后是约束类型。例如:int只有在组件的URL在正确位置包含有效的整数值时,才会匹配该组件的URL。

  1. @page "/"
  2. @page "/purchase-order/{OrderNumber:int}"
  3. <h1>
  4. Order number:
  5. @if (!OrderNumber.HasValue)
  6. {
  7. @:None
  8. }
  9. else
  10. {
  11. @OrderNumber
  12. }
  13. </h1>
  14. <h3>Select a purchase order</h3>
  15. <ul>
  16. <li><a href="/purchase-order/1/">Order 1</a></li>
  17. <li><a href="/purchase-order/2/">Order 2</a></li>
  18. <li><a href="/purchase-order/42/">Order 42</a></li>
  19. </ul>
  20. @code {
  21. [Parameter]
  22. public int? OrderNumber { get; set; }
  23. }

Constraint types

Constraint .NET type Valid Invalid
:bool System.Boolean
- true
- false

- 1
- Hello
:datetime System.DateTime
- 2001-01-01
- 02-29-2000

- 29-02-2000
:decimal System.Decimal
- 2.34
- 0.234

- 2,34
- ૦.૨૩૪
:double System.Double
- 2.34
- 0.234

- 2,34
- ૦.૨૩૪
:float System.Single
- 2.34
- 0.234

- 2,34
- ૦.૨૩૪
:guid System.Guid
- 99303dc9-8c76-42d9-9430-de3ee1ac25d0

- {99303dc9-8c76-42d9-9430-de3ee1ac25d0}
:int System.Int32
- -1
- 42
- 299792458

- 12.34
- ૨૩
:long System.Int64
- -1
- 42
- 299792458

- 12.34
- ૨૩

Localization

Blazor约束当前不支持本地化。

  • 只有格式为0..9的数字才被认为是有效的,而不是来自非英语语言,如૦..૯(古吉拉特语)。
  • 日期的格式仅为MM-dd-yyyyMM-dd-yy或ISO格式yyyy-MM-dd
  • 布尔值必须为truefalse

    Unsupported constraint types

    Blazor约束不支持以下约束类型,但希望将来会支持:

  • 贪婪参数 Greedy parameters

在ASP.NETMVC中,可以提供以星号开头的参数名称,并捕获包含正斜杠的URL块。
/articles/{Subject}/{*TheRestOfTheURL}

  • 正则表达式

Blazor当前不支持基于正则表达式约束参数的功能。

  • 枚举

目前不可能将参数约束为与枚举的值匹配。

  • 自定义约束

无法定义自定义类来确定传递给参数的值是否有效。

Optional router parameters

Blazor不显式地支持可选的路由参数,但是通过在组件上添加多个@page声明就可以很容易地实现等效的路由参数。例如,更改标准的Counter.razor页面以添加额外的URL。

  1. @page "/counter"
  2. @page "/counter/{CurrentCount:int}"

int currentCount字段更改为参数,如下所示
[Parameter] public int CurrentCount { get; set; }
然后用CurrentCount替换对currentCount的所有引用。还可以在页面上添加一些导航,以便我们可以快速测试我们的路线

  1. @page "/counter"
  2. @page "/counter/{CurrentCount:int}"
  3. <h1>Counter</h1>
  4. <p>Current count: @CurrentCount</p>
  5. <button class="btn btn-primary" @onclick=IncrementCount>Click me</button>
  6. <ul>
  7. <li><a href="/counter/42">Navigate to /counter/42</a></li>
  8. <li><a href="/counter/123">Navigate to /counter/123</a></li>
  9. <li><a href="/counter/">Navigate to /counter</a></li>
  10. </ul>
  11. @code {
  12. [Parameter]
  13. public int CurrentCount { get; set; }
  14. void IncrementCount()
  15. {
  16. CurrentCount++;
  17. }
  18. }

当我们运行这个应用程序时,我们看到我们可以导航到/Counter(不需要参数)或/Counter/AnyNumber(指定了参数值)。当URL中没有指定值时,将使用属性类型的默认值。

Specifying a default value for optional parameters

如果我们希望参数的默认值不是C#默认值,该怎么办呢?例如,当没有为CurrentCount指定值时,我们可能希望它默认为1而不是0
首先,我们需要将参数属性的类型更改为可为空,这样我们就可以区分/Counter/0和Just/Counter-之间的区别,然后如果该属性为空,则将默认值分配给该属性。

  1. [Parameter]
  2. public int? CurrentCount { get; set; }
  3. protected override void OnInitialized()
  4. {
  5. base.OnInitialized();
  6. CurrentCount = CurrentCount ?? 1;
  7. }

乍一看,这似乎是可行的,导航到/Counter实际上会将CurrentCount值默认为1
image.png
但是,这仅在第一次显示页面时起作用。如果我们现在使用其中一个链接导航到/Counter,而没有首先导航到另一个页面(比如Home),我们将看到CurrentCount缺省为NULL。
Routing - 图3
当组件是@page并且Blazor应用程序导航到呈现同一页面的新URL时,Blazor不会创建组件的新实例来呈现页面,而是将其视为参数已更改的相同页面。因此,只有在第一次创建页面时才会执行OnInitialized。有关详细信息,请参见组件生命周期。

Previous URL Current URL Counter.OnInit executed
/ /counter Yes – Different page
/counter /counter/42 No – Same page
/counter/42 counter/123 No – Same page
/counter/123 /counter No – Same page
/counter /counter/123 No – Same page
/counter/123 /counter No – Same page
/counter / Yes – Different page

正确的解决方案是默认SetParametersAsync中的值-只要参数更改,并且它们的值被推入组件的属性中(例如在导航期间),就会调用该方法。

  1. [Parameter]
  2. public int? CurrentCount { get; set; }
  3. public async override Task SetParametersAsync(ParameterView parameters)
  4. {
  5. await base.SetParametersAsync(parameters);
  6. CurrentCount = CurrentCount ?? 1;
  7. }

Routing - 图4

404 - Not found

当Blazor无法将URL与组件匹配时,我们可能希望告诉它要显示什么内容。
Router组件有一个名为NotFoundRenderFragment参数,它是一个RenderFragment。当尝试访问路由器无法与任何组件匹配的URL时,将显示在Router组件的此参数中定义的任何Razor标记。

  1. <Router AppAssembly="typeof(Program).Assembly">
  2. <Found Context="routeData">
  3. <RouteView RouteData="routeData" />
  4. </Found>
  5. <NotFound>
  6. <div class="content">
  7. <h1>PAGE NOT FOUND</h1>
  8. <p>
  9. The page you have requested could not be found. <a href="/">Return to the home page.</a>
  10. </p>
  11. </div>
  12. </NotFound>
  13. </Router>

image.png

Navigating our app via HTML

链接到Blazor组件中的路由的最简单方法是使用HTML超链接。

  1. <a href="/Counter">This works just fine</a>

Blazor组件中的超链接会被自动截取。当用户单击超链接时,浏览器不会向服务器发送请求,而是Blazor将更新浏览器中的URL,并呈现与新地址相关联的任何页面。

Using the NavLink component

Blazor还包括一个用于呈现超链接的组件,该组件还支持在Address与URL匹配时更改HTML元素的CSS类。
如果我们查看默认Blazor应用程序中的/Shared/NavMenu.razor组件,我们将看到如下所示的标记:

  1. <NavLink class="nav-link" href="counter">
  2. <span class="oi oi-home" aria-hidden="true"></span> Counter
  3. </NavLink>

NavLink组件使用HTML超链接装饰其子内容。所有属性(如classhref等)都通过属性Splatting直接呈现给<a>元素。NavLink组件有两个参数可提供附加行为。
ActiveClass参数指定当浏览器的URL与href属性的URL匹配时,将哪个CSS类应用于呈现的<a>元素。如果未指定,Blazor将应用名为“active”的CSS类。
Routing - 图6

URL matching

Match参数标识应该如何将浏览器的URL与href进行比较,以便决定是否应该将ActiveClass添加到元素的class属性。
在新的Blazor应用程序中编辑/Pages/Counter.razor文件,以便可以从三个URL访问该文件。

  1. @page "/counter"
  2. @page "/counter/1"
  3. @page "/counter/2"

然后编辑/Shared/NavMenu.razor组件,使计数器菜单项有两个子菜单链接。

  1. <li class="nav-item px-3">
  2. <NavLink class="nav-link" href="counter" Match=@NavLinkMatch.All>
  3. <span class="oi oi-plus" aria-hidden="true"></span>Counter
  4. </NavLink>
  5. <ul class="nav flex-column">
  6. <li class="nav-item px-3">
  7. <NavLink class="nav-link" href="counter/1" Match=@NavLinkMatch.All>
  8. <span class="oi oi-plus" aria-hidden="true"></span>Counter/1
  9. </NavLink>
  10. </li>
  11. <li class="nav-item px-3">
  12. <NavLink class="nav-link" href="counter/2" Match=@NavLinkMatch.All>
  13. <span class="oi oi-plus" aria-hidden="true"></span>Counter/2
  14. </NavLink>
  15. </li>
  16. </ul>
  17. </li>

还要编辑/wwwroot/site.css并添加以下内容,这样我们就可以很容易地看到哪些NavLink元素被认为是“活动的”。

  1. .nav-item a.active::after
  2. {
  3. content: " *";
  4. margin-left: 1em;
  5. }

这三个NavLink组件导航到/COUNTER/COUNTER/1/COUNTER/2,如果我们运行应用程序并单击各种链接,我们将看到以下内容。
Routing - 图7

NavLinkMatch

NavLink组件的Match参数接受类型为NavLinkMatch的值。这将告诉NavLink组件如何将浏览器的URL与它呈现的<a>元素的href属性进行比较,以确定它们是否相同。
在前面的示例中,我们为每个NavLink组件上的Match参数指定了NavLinkMatch.All。这意味着我们希望Blazor仅在其href与浏览器的URL完全匹配时才将每个NavLink视为活动的。如果我们现在更改链接到/COUNTERNavLink,使其Match参数为NavLinkMatch.Prefix,我们将看到每当URL以/COUNTER开头时,它都将被视为匹配,因此它还将匹配/COUNTER/1/COUNTER/2
要说明不同之处,请在/Shared/NavMenu.razor的代码部分中声明一个字段

  1. NavLinkMatch MatchMode = NavLinkMatch.All;

查找<div class="@NavMenuCssClass"...元素,并在<ul>元素之前添加以下标记以将<select>绑定到新字段。

  1. <select @bind=MatchMode class="form-control">
  2. <option value=@NavLinkMatch.All>All</option>
  3. <option value=@NavLinkMatch.Prefix>Prefix</option>
  4. </select>

最后,找到其href链接到/counterNavLink,并将其匹配参数更改为@MatchMode。您的代码现在应该如下所示。

  1. <div class="top-row pl-4 navbar navbar-dark">
  2. <a class="navbar-brand" href="">NavigationViaHtml</a>
  3. <button class="navbar-toggler" @onclick=ToggleNavMenu>
  4. <span class="navbar-toggler-icon"></span>
  5. </button>
  6. </div>
  7. <div class="@NavMenuCssClass" @onclick=ToggleNavMenu>
  8. <select @bind=MatchMode class="form-control">
  9. <option value=@NavLinkMatch.All>All</option>
  10. <option value=@NavLinkMatch.Prefix>Prefix</option>
  11. </select>
  12. <ul class="nav flex-column">
  13. <li class="nav-item px-3">
  14. <NavLink class="nav-link" href="" Match=@NavLinkMatch.All>
  15. <span class="oi oi-home" aria-hidden="true"></span> Home
  16. </NavLink>
  17. </li>
  18. <li class="nav-item px-3">
  19. <NavLink class="nav-link" href="counter" Match=@MatchMode>
  20. <span class="oi oi-plus" aria-hidden="true"></span>Counter
  21. </NavLink>
  22. <ul class="nav flex-column">
  23. <li class="nav-item px-3">
  24. <NavLink class="nav-link" href="counter/1" Match=@NavLinkMatch.All>
  25. <span class="oi oi-plus" aria-hidden="true"></span>Counter/1
  26. </NavLink>
  27. </li>
  28. <li class="nav-item px-3">
  29. <NavLink class="nav-link" href="counter/2" Match=@NavLinkMatch.All>
  30. <span class="oi oi-plus" aria-hidden="true"></span>Counter/2
  31. </NavLink>
  32. </li>
  33. </ul>
  34. </li>
  35. </ul>
  36. </div>
  37. @code {
  38. NavLinkMatch MatchMode = NavLinkMatch.All;
  39. bool collapseNavMenu = true;
  40. string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
  41. void ToggleNavMenu()
  42. {
  43. collapseNavMenu = !collapseNavMenu;
  44. }
  45. }

选择COUNTER/1COUNTER/2链接,切换<select>的值。
Routing - 图8
尽管浏览器URL保持不变,但我们可以看到第一个计数器NavLink根据其Match参数的设置在活动/非活动之间切换。

Navigating our app via code

从Blazor访问浏览器导航是通过NavigationManager服务提供的。这可以使用razor文件中的@Inject或CS文件中的[Inject]属性注入到Blazor组件中。
NavigationManager服务有两个特别感兴趣的成员:NavigateToLocationChanged
LocationChanged事件将在检测导航事件中详细说明。

The NavigateTo method

NavigationManager.NavigateTo方法使C#代码能够控制浏览器的URL。与截取的导航一样,浏览器实际上并不导航到新的URL。取而代之的是替换浏览器中的URL,并将先前的URL插入到浏览器的导航历史记录中,但不会向服务器请求新页面的内容。通过NavigateTo进行的导航将触发LocationChanged事件,为IsNavigationIntercepted传递新URL和false
对于本例,我们将再次更改标准Blazor模板。我们将使用前面在路由参数可选路由参数中学到的内容。
首先,删除Index.razorFetchData.razor页面,并删除NavMenu.razor文件中指向这些页面的链接。同样在NavMenu中,将链接的href更改为counter为href="",因为我们将使其成为默认页面。
编辑Counter.razor并为其提供两条路由,"/""/count/{CurrentCount:int}",还要确保它是从CounterBase类派生出来的,这样我们就可以在浏览器的控制台窗口中看到导航日志—前面在OnLocationChanged部分概述了CounterBase.cs文件。

  1. @page "/"
  2. @page "/counter/{CurrentCount:int}"
  3. @inherits CounterBase

我们还需要更改currentCount字段,使其成为具有getter和setter的属性,并将其修饰为[Parameter]。请注意,它也已从CamelCase重命名为PascalCase

  1. [Parameter]
  2. public int CurrentCount { get; set; }

我们现在有了一个计数器页面,可以简单地访问应用程序的主页,也可以通过指定/Counter/X来访问,其中X是整数值。
NavigationManager被注入到我们的CounterBase类中,因此可以在我们的Counter.razor文件中进行访问。

  1. @code {
  2. [Parameter]
  3. public int CurrentCount { get; set; }
  4. bool forceLoad;
  5. void AlterBy(int adjustment)
  6. {
  7. int newCount = CurrentCount + adjustment;
  8. UriHelper.NavigateTo("/counter/" + newCount, forceLoad);
  9. }
  10. }

我们将从两个按钮调用AlterBy方法,一个用于递增CurrentCount,另一个用于递减CurrentCount。用户还可以选择一个选项forceLoad,它将在NavigateTo调用中设置相关参数,以便我们可以看到差异。整个文件最终应该如下所示:

  1. @page "/"
  2. @page "/counter/{CurrentCount:int}"
  3. @implements IDisposable
  4. @inject NavigationManager NavigationManager
  5. <h1>Counter value = @CurrentCount</h1>
  6. <div class="form-check">
  7. <input @bind=@forceLoad type="checkbox" class="form-check-input" id="ForceLoadCheckbox" />
  8. <label class="form-check-label" for="ForceLoadCheckbox">Force page reload on navigate</label>
  9. </div>
  10. <div class="btn-group" role="group">
  11. <button @onclick=@( () => AlterBy(-1) ) class="btn btn-primary">-</button>
  12. <input value=@CurrentCount readonly class="form-control" />
  13. <button @onclick=@( () => AlterBy(1) ) class="btn btn-primary">+</button>
  14. </div>
  15. <a class="btn btn-secondary" href="/Counter/0">Reset</a>
  16. <p>
  17. <em>Page redirects to ibm.com when count hits 10!</em>
  18. </p>
  19. @code {
  20. [Parameter]
  21. public int CurrentCount { get; set; }
  22. bool forceLoad;
  23. void AlterBy(int adjustment)
  24. {
  25. int newCount = CurrentCount + adjustment;
  26. if (newCount >= 10)
  27. NavigationManager.NavigateTo("https://ibm.com");
  28. NavigationManager.NavigateTo("/counter/" + newCount, forceLoad);
  29. }
  30. protected override void OnInitialized()
  31. {
  32. // Subscribe to the event
  33. NavigationManager.LocationChanged += LocationChanged;
  34. base.OnInitialized();
  35. }
  36. private void LocationChanged(object sender, LocationChangedEventArgs e)
  37. {
  38. string navigationMethod = e.IsNavigationIntercepted ? "HTML" : "code";
  39. System.Diagnostics.Debug.WriteLine($"Notified of navigation via {navigationMethod} to {e.Location}");
  40. }
  41. void IDisposable.Dispose()
  42. {
  43. // Unsubscribe from the event when our component is disposed
  44. NavigationManager.LocationChanged -= LocationChanged;
  45. }
  46. }

单击-+按钮将调用AlterBy方法,该方法将指示NavigationManager服务导航到/counter/X,其中X是调整后的CurrentCount的值-在浏览器控制台中产生以下输出:

WASM: Notified of navigation via code to http://localhost:6812/counter/1
WASM: Notified of navigation via code to http://localhost:6812/counter/2
WASM: Notified of navigation via code to http://localhost:6812/counter/3
WASM: Notified of navigation via code to http://localhost:6812/counter/4

单击Reset链接将导致截获的导航(即,不是用C#代码启动的),并导航到/counter/0,重置CurrentCount的值。

WASM: Notified of navigation via code to http://localhost:6812/counter/1
WASM: Notified of navigation via code to http://localhost:6812/counter/2
WASM: Notified of navigation via code to http://localhost:6812/counter/3
WASM: Notified of navigation via code to http://localhost:6812/counter/4
WASM: Notified of navigation via HTML to http://localhost:6812/Counter/0

ForceLoad

forceLoad参数指示Blazor绕过其自己的路由系统,让浏览器实际导航到新的URL。这将导致向服务器发出HTTP请求,以检索要显示的内容。
请注意,导航到 off-site URL不需要强制加载。对另一个域调用NavigateTo将调用完整的浏览器导航。
使用本节中的GitHub示例。在浏览器的控制台窗口中查看IsNavigationIntercepted在通过按钮和重置链接导航时的不同之处,并在浏览器的网络窗口中查看根据您是否处于以下状态,它的行为有何不同:

  • Navigating with forceLoad set to false.
  • Navigating with forceLoadset to true.
  • Navigating to an off-site URL.

要观察最后一个场景,您可能希望更新AdustBy方法,以便在CurrentValue传递特定值时进行异地导航。

  1. void AlterBy(int adjustment)
  2. {
  3. int newCount = CurrentCount + adjustment;
  4. if (newCount >= 10)
  5. NavigationManager.NavigateTo("https://ibm.com");
  6. NavigationManager.NavigateTo("/counter/" + newCount, forceLoad);
  7. }

Detecting navigation events

从Blazor访问浏览器导航是通过NavigationManager服务提供的。这可以使用razor文件中的@Inject或CS文件中的[Inject]属性注入到Blazor组件中。

The LocationChanged event

LocationChanged是每当浏览器中的URL发生更改时触发的事件。它传递一个LocationChangedEventArgs实例,该实例提供以下信息:

  1. public readonly struct LocationChangedEventArgs
  2. {
  3. public string Location { get; }
  4. public bool IsNavigationIntercepted { get; }
  5. }

Location属性是浏览器中显示的完整URL,包括协议、路径和任何查询字符串。
IsNavigationIntercepted指示导航是通过代码还是通过HTML导航启动的。

  • false

导航是由代码调用的NavigationManager.NavigateTo启动的。

  • true

用户单击HTML导航元素(如a href),Blazor拦截导航,而不是允许浏览器实际导航到新的URL,这将导致对服务器的请求。在其他情况下也是如此,比如如果页面上的某些JavaScript导致导航(例如,在超时之后)。最终,任何不是通过NavigationManager.NavigateTo发起的导航事件都将被视为拦截的导航,并且此值将为true

注意:当前没有办法拦截导航并阻止其继续进行。

Observing OnLocationChanged events

需要注意的是,NavigationManager服务是一个长期存在的实例。因此,在服务的生命周期内,订阅其LocationChanged事件的任何组件都将被强引用。因此,重要的是,我们的组件在被销毁时也要取消订阅此事件,否则它们将不会被垃圾收集。
目前,ComponentBase类没有销毁时间的生命周期事件,但可以实现IDisposable接口。

  1. @implement IDisposable
  2. @inject NavigationManager NavigationManager
  3. protected override void OnInitialized()
  4. {
  5. // Subscribe to the event
  6. NavigationManager.LocationChanged += LocationChanged;
  7. base.OnInitialized();
  8. }
  9. void LocationChanged(object sender, LocationChangedEventArgs e)
  10. {
  11. string navigationMethod = e.IsNavigationIntercepted ? "HTML" : "code";
  12. System.Diagnostics.Debug.WriteLine($"Notified of navigation via {navigationMethod} to {e.Location}");
  13. }
  14. void IDisposable.Dispose()
  15. {
  16. // Unsubscribe from the event when our component is disposed
  17. NavigationManager.LocationChanged -= LocationChanged;
  18. }