第二章 基础知识(1) - 路由和导航

路由

Blazor使用名为Router的专用组件来实现路由请求,以Blazor web app auto项目为例,Router组件配置如下:

  • Routers.razor

    <Router AppAssembly="typeof(Program).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
            <FocusOnNavigate RouteData="routeData" Selector="h1" />
        </Found>
    </Router>
    

路由原理

当在组件中使用 @page 指令指定路由地址后,在编译时会使用[RouteAttribute]特性为生成的组件类进行声明,来指定组件的路由模板。

应用启动时,Blazor会检查Router组件的AppAssemblyAdditionalAssemblies等属性,以了解它应扫描哪些程序集。然后通过扫描这些程序集来寻找具有[RouteAttribute]特性的类型(组件),并根据[RouteAttribute]中的值(即组件的路由路径)构建一个 RouteData 对象来告诉 Blazor 应该如何映射、路由参数传递、处理等等。

路由的布局

可以通过在RouteView组件中使用DefaultLayout设置默认的布局组件。

默认情况下,Blazor web项目中指定MainLayout.razor作为应用的默认布局,如果希望在某个组件中使用指定的布局组件,可以在组件中使用@layout 指令。(后面有专门讲解布局的章节)

在这里插入图片描述

FocusOnNavigate

<FocusOnNavigate>组件用于在路由后聚焦到组件上的指定元素上。

Found

在进行导航时,当存在匹配的路由,<Found>组件会展示路由到的路由视图组件,即RouteView组件。RouteView实例会从Route上接收到RouteData对象、路由参数或URL中的任何参数,然后渲染指定的组件与布局。

NotFound

<Router>组件中,还可使用 <NotFound> 组件指定在不存在匹配路由时返回给用户的内容。

  • 尝试后发现<NotFound> 仅在增强导航(内部导航)时有效,如果是完整的网页请求,直接就到404了

  • 示例

    <Router AppAssembly="typeof(Program).Assembly">
        <Found Context="routeData">
            <!--指定布局视图-->
            <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
    				<!--聚焦到指定的元素上,Selector使用CSS的选择器-->
            <FocusOnNavigate RouteData="routeData" Selector="h1"/>
        </Found>
        <NotFound>
            <LayoutView Layout="typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    

Navigating

<Navigating>组件可以用于导航的执行过程中由于网络缓慢,或执行耗时任务时(例如OnNavigateAsyncawait异步执行过程中)先给用户展示一定的内容,以优化用户体验。

  • 尝试后发现,在 Blazor Web App项目的Per/component渲染配置模式下无效,其原因在于这种模式下,Routes.razor组件默认为静态渲染模式

  • 示例

    <Router AppAssembly="typeof(Program).Assembly" OnNavigateAsync="NavigateHandler">
        <Navigating>
            <div style="padding:20px;background-color:blue;color:white">
                <p>正在加载中......</p>
            </div>
        </Navigating>
        <Found Context="routeData">
            <!--指定布局视图-->
            <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
    				<!--聚焦到指定的元素上,Selector使用CSS的选择器-->
            <FocusOnNavigate RouteData="routeData" Selector="h1"/>
        </Found>
    </Router>
    
    @code {
        private async Task NavigateHandler(NavigationContext context)
        {
            await Task.Delay(3000);
        }
    }
    

一、路由模板的使用

路由模板的使用很简单,只需要在组件上直接通过@page指令来进行模板的定义即可,组件支持使用多个 @page 指令的多个路由模板。

@page "/blazor-route"
@page "/different-blazor-route"

<PageTitle>Routing</PageTitle>

<h1>Routing Example</h1>

二、路由参数

1、路由参数

可以在路由模板中通过使用{}符号设置路由参数,路由器会将路由参数填充到具有相同名字且使用[Parameter]特性定义的组件属性上。

  • 路由参数是不区分大小写的

  • 示例

    @page "/route-parameter/{text}"
    
    <p>Blazor is @Text!</p>
    
    @code {
        [Parameter]
        public string? Text { get; set; }
    }
    

在这里插入图片描述

2、URL参数

上面说了路由参数的使用,但实际上在项目开发过程中很少这么去用,很多情况下浏览器进行访问时,URL中的参数是使用?来与访问路径进行区分,用&来拼接多个参数,并且以键值对的形式传递的,例如:localhost:5249/parameterTest?Name=Schuyler&Age=23。很显然,想要获取这里的参数,使用路由参数明显是不合适的。

针对这种情况,.net core组件库提供了[SupplyParameterFromQuery]特性,用于将Url中的参数填充到对应的属性上。默认情况下是相同名字(不区分大小写)来进行参数和属性之间的匹配的。也可以在使用[SupplyParameterFromQuery]特性时为属性设置指定的参数名称[SupplyParameterFromQuery(Name="ParamName")]

  • 示例

    @page "/parameterTest"
    @rendermode InteractiveAuto
    
    <h3>ParameterTest</h3>
    <p>Name: @Name</p>
    <p>Age: @Age</p>
    <p>Score: @Score</p>
    
    @code {
        [SupplyParameterFromQuery]
        public string? Name { get; set; }
        [SupplyParameterFromQuery]
        public int? Age { get; set; }
        [SupplyParameterFromQuery(Name = "sco")]
        public int Score { get; set; }
    }
    

在这里插入图片描述

三、路由约束

1、可选参数

如果希望路由参数可选,直接在参数名后面加一个?就可以了,例如@page "/route-parameter/{text?}"

2、类型约束

路由约束强制在路由组件时进行参数的类型匹配,如果路径上的参数类型不符合,则不匹配。

语法:{param:type}

  • 可选参数同样可以使用约束。

  • 可用的约束类型

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

    @page "/user/{id:int}/{option:bool?}"
    @rendermode InteractiveAuto
    
    <p>
        Id: @Id
    </p>
    
    <p>
        Option: @Option
    </p>
    
    @code {
        [Parameter]
        public int Id { get; set; }
    
        [Parameter]
        public bool Option { get; set; }
    }
    

3、范围约束

数字范围

对于数字类型,可以使用min()max()range()等方法进行对应的范围约束。

  • 示例

    @page "/paramTest/{id:int:min(1):max(100)}"
    
    <h3>ParamTest</h3>
    <h4>@Id</h4>
    
    @code {
        [Parameter]
        public int Id { get; set; }
    }
    

字符长度

对于字符串长度,可以使用minlength()maxlength()length()等方法进行字符长度的约束。

  • 示例

    @page "/paramTest/{id:int:minlength(1):maxlength(3)}"
    @page "/paramTest/{id:int:length(5,7)}"
    
    <h3>ParamTest</h3>
    <h4>@Id</h4>
    
    @code {
        [Parameter]
        public int Id { get; set; }
    }
    

4、正则约束

对于路由参数,可以使用regex()方法进行正则约束,除了regex()方法外,ASP.net Core还提供了多个简便的内容约束关键字,分别如下:

  • alpha:路由参数只能包含大小写字母。

  • guid:路由参数内容要匹配GUID值

  • 示例

    @page "/paramTest/{data:alpha}"
    @* @page "/paramTest/{data:guid}" *@
    @* @page "/paramTest/{data:regex(^[[a-z]]{{3}}-[[0-9]]{{4}}$)}" *@
    
    <h3>ParamTest</h3>
    <h4>@Data</h4>
    
    @code {
        [Parameter]
        public string Data { get; set; }
    }
    

需要注意的是,由于[]{}与路由模板的占位符和通配符有冲突,所以使用正则字符串时注意用[[]]{{}}来替换。

5、catch-all参数

catch-all参数其实就是通配符参数,将获URL上对应位置往后的所有的路径内容作为参数值。

  • 语法:{*paramName}

  • 位于URL的末尾

  • 必须有对应的使用了[Parameter]特性的组件属性,且为string类型。

  • 示例

    @page "/cat-chall-test/{*pageRoute}"
    
    <p>
        PageRoute: @PageRoute
    </p>
    
    @code {
        [Parameter]
        public string? PageRoute { get; set; }
    }
    

四、跨程序集路由

Blazor中支持静态路由器和交互式路由器:

  • 静态路由器依赖于在应用启动时定义的路由信息,并通过 HTTP 请求来加载相应的页面,常用于传统的页面导航
  • 交互式路由器则能够在浏览器中动态根据当前 URL 变化来更新视图,增强了用户体验,使得页面切换更加流畅和快速,而不会带来完整的页面刷新

这种机制使得 Blazor 应用能够在用户浏览时表现得更加灵活和响应迅速,同时降低了频繁的服务器请求,提高了性能和速度。

静态路由

如果组件没有禁用预渲染,组件进行静态服务端渲染期间所使用的路由称为静态路由(Router 组件,即Routes.razor 中的 <Router>)。

如果在其他项目中的组件,需要进行静态渲染,静态渲染是在服务端进行的,因此必须将其他程序集中的要进行静态渲染的可路由组件披露给服务端,此时需要通过在Program中,使用AddAdditionalAssemblies方法来实现。

例如Blazor web app Auto项目下,打开服务端项目的Program类,可以看到如下语句:

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddInteractiveWebAssemblyRenderMode()
    .AddAdditionalAssemblies(typeof(BlazorAuto.Client._Imports).Assembly);

其中AddAdditionalAssemblies(typeof(BlazorAuto.Client._Imports).Assembly就表示将客户端的程序集的可路由组件包含到服务端中,让服务端在进行静态路由时,可以路由到对应的可路由组件。

交互式路由

交互式组件在进行静态路由后,路由器将在服务器上的路由转变为交互式,此后所使用的路由称为“交互式路由”。

交互式路由的内部导航不涉及从服务器请求新页面内容。 因此,内部页面请求不会发生预渲染。

Router 组件有AdditionalAssemblies属性,可以添加其他包含组件的额外程序集,当进行增强型导航时,Blazor 能够根据这些信息找到合适的组件进行渲染。

<Router
    AppAssembly="..."
    AdditionalAssemblies="new[] { typeof(BlazorSample.Client._Imports).Assembly }">
    ...
</Router>

总结

对于AddAdditionalAssemblies()方法和AdditionalAssemblies属性可以简单的理解为:

  • AddAdditionalAssemblies()的添加,可以让非增强型导航顺利访问到组件
  • AdditionalAssemblies属性的设置,可以让增强型导航顺利访问到组件

因此,通常情况下,最好同时设置 AddAdditionalAssemblies 方法和 AdditionalAssemblies 属性。

导航

一、NavigationManager

C# 代码中使用NavigationManager类型来管理 URI 和导航。 如果在组件中使用,可以通过@inject NavigationManager Navigation注入。

1、常用属性

Uri:获取当前绝对 URI。

BaseUri:获取可在相对 URI 路径之前添加用于生成绝对 URI 的基 URI(带有尾部反斜杠)。

  • 一般情况下,BaseUri对应于文档的 <base> 元素(App.razor中,<head> 内容的位置)上的 href 属性。

2、常用事件

订阅事件(导航后)

LocationChanged:增强型导航位置更改时触发的事件(导航完成后)。

  • 注意这里的导航位置更改指的是发生增强型导航时,例如调用NavigateTo方法进行导航(且增强型导航可用时)、以及浏览器上前进后退(且增强型导航可用时)、选择内部链接时(例如<a>标签、<form>表单指向内部链接)等等。

  • 事件触发时会传入两个参数,一个是触发者对象,另一个是LocationChangedEventArgs对象,其中LocationChangedEventArgs对象提供了如下信息。

    • Location:新位置的 URL
    • IsNavigationIntercepted:只读,如果为true,表示用户通过单击链接导航到目标 URI(官方提及到,这种被认为是Blazor 拦截了浏览器中的导航)。 如果为false,则表示是通过NavigateTo()以代码的方式导航到目标URI
  • 示例

    @page "/navigate"
    @rendermode InteractiveAuto
    @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;
        }
    }
    

注册导航处理程序(导航前)

IDisposable RegisterLocationChangingHandler(Func<LocationChangingContext, ValueTask> locationChangingHandler):注册一个处理程序来处理传入的内部导航事件,在导航发生前触发(不管是不是增强型导航),可阻止导航或进一步转向。

  • LocationChangingContext:导航上下文对象,其中提供了如下属性和方法。
    • TargetLocation:获取目标位置,调用NavigateTo时传入了什么就是什么。
    • HistoryEntryState:获取与目标历史记录条目关联的状态。
    • IsNavigationIntercepted:获取是否从链接截获了导航。
    • CancellationToken:获取CancellationToken以确定导航是否已取消,例如,确定用户是否触发了不同的导航。
    • PreventNavigation():调用以阻止导航继续。
  • IDisposable:返回对象,用于释放处理程序,取消注册,以允许组件进行垃圾回收。

通常,会在组件的OnAfterRender()方法或OnAfterRendeAsync()方法中使用注册导航处理程序。

  • 示例

    @page "/nav-handler"
    @rendermode InteractiveAuto
    @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();
    }
    

除了上述例子中进行导航的拦截取消外,还可以通过NavigationLock组件来实现,具体可以内置组件章节的内容。

3、常用方法

导航

NavigateTo(string uri,bool forceLoad=false,bool replace=false):导航到指定URI。

  • uri:目标组件的路径,前缀可以带/,也可以不带
  • forceLoad:如果为true,则绕过客户端路由并强制浏览器从服务器加载新页面,无论增强型导航是否可用。为false时,如果当前 URL 中可使用增强型导航,则使用增强型导航,否则对请求的 URL 执行整页重载。
  • replace:如果为true,则替换浏览器历史记录中的当前 URI。如果为false,则将新的URI追加到历史堆栈中。

NavigateTo(string uri, NavigationOptions options):导航到指定URI,用法跟上面的方法差不多的,不过是将参数封装到options对象中。

  • options:选项参数,可以设置ForceLoadReplaceHistoryEntryHistoryEntryState三个属性,使用这个方法主要是可以设置HistoryEntryState,不然直接用上面那个方法就可以了。HistoryEntryState 属性用于向浏览器的历史记录中添加与当前转向目标 URL 相关联的状态值。这使得在不同组件之间能够实现简单的数据传递。常用于传递原来的URI,方便处理完后跳回上一次URI

  • 示例

    @page "/navigate"
    @rendermode InteractiveAuto
    @inject NavigationManager Navigation
    
    <h1>Navigate Example</h1>
    
    <button class="btn btn-primary" @onclick="NavigateToCounterComponent">
        Navigate to the Counter component
    </button>
    
    @code {
        private void NavigateToCounterComponent()
        {
            Navigation.NavigateTo("counter");
        }
    }
    

刷新

Refresh(bool forceReload = false):刷新当前页面。如果增强型导航可用,则执行增强型导航,否则执行整页重载。

  • forceReload:如果为true,则绕过客户端路由并强制浏览器从服务器加载新页面,无论增强型导航是否可用。
  • 如果执行的是增强型导航,则会保留当前页面的数据,否则,会全部重置。

导航URI处理

ToAbsoluteUri(string relativeUri):将相对 URI 转换为绝对 URI。

ToBaseRelativePath(string uri):根据应用的基URI,将绝对URI转换为相对URI。

  • 例如,基URI为https://localhost:8000,绝对URI为https://localhost:8000/segment1/segment2,则相对于基URI的相对URI为segment1/segment2
  • 如果基URI不匹配会引发异常。

string GetUriWithQueryParameter(string name, string? value):给当前URI添加指定参数,生成新的URI并返回。

  • 这个方法有很多个重载,用于传入不同类型的参数值。
  • 对于URI中已经存在的参数可以进行替换,不存在的参数则添加,如果将参数的值设为null则表示删除URI中的参数。
  • 其参数值支持的类型与约束所支持的类型是相同的。

string GetUriWithQueryParameter([string uri,] IReadOnlyDictionary<string, object?> parameters):给当前URI或指定的URI添加指定参数,生成新的URI并返回。

  • 用法跟GetUriWithQueryParameter差不多,不过可以指定URI,且传入的参数名和值,是以字典对象的形式。

注册导航处理程序

IDisposable RegisterLocationChangingHandler(Func<LocationChangingContext, ValueTask> locationChangingHandler):注册一个处理程序来处理传入的内部导航事件。发生增强型导航时会调用处理程序。

  • LocationChangingContext:导航上下文对象,其中提供了如下属性和方法。

    • TargetLocation:获取目标位置,调用NavigateTo时传入了什么就是什么。
    • HistoryEntryState:获取与目标历史记录条目关联的状态。
    • IsNavigationIntercepted:获取是否从链接截获了导航。
    • CancellationToken:获取CancellationToken以确定导航是否已取消,例如,确定用户是否触发了不同的导航。
    • PreventNavigation():调用以阻止导航继续。
  • IDisposable:返回对象,用于释放处理程序,取消注册,以允许组件进行垃圾回收。

  • 示例

    @page "/nav-handler"
    @rendermode InteractiveAuto
    @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();
    }
    

除了上述例子中进行导航的拦截取消外,还可以通过NavigationLock 组件来实现,具体可以内置组件章节的内容。

二、全局导航监听

Router组件有OnNavigateAsync异步事件,在整个 Blazor 应用程序中,只要有任意导航位置发生更改,就会触发OnNavigateAsync事件,因此可以通过OnNavigateAsync事件实现全局导航监听。

  • 测试后发现,只要发生路由组件的访问,就会触发OnNavigateAsync事件
  • 需要注意的是,首次访问时,由于服务器会进行预渲染,所以OnNavigateAsync事件会触发两次

目前学习了可以对导航进行监控的三种方式:OnNavigateAsync事件、NavigationManagerLocationChanged事件、NavigationManagerRegisterLocationChangingHandler()方法。

  • 其执行顺序为:RegisterLocationChangingHandler()OnNavigateAsyncLocationChanged

NavigationContext

OnNavigateAsync事件会接收一个NavigationContext对象参数,该对象中含有如下成员:

  • Path:用于获取将要导航到的目标 URL 路径

  • CancellationToken:取消令牌,表示当前导航是否已被取消

  • 示例

    <Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new [] {typeof(CustomRazor._Imports).Assembly}" OnNavigateAsync="NavigateHandler">
        <Found Context="routeData">
            <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
            <FocusOnNavigate RouteData="routeData" Selector="h1" />
        </Found>
        <NotFound>
            <LayoutView Layout="typeof(Layout.MainLayout)">
                <p>找不到对应的组件</p>
            </LayoutView>
        </NotFound>
    </Router>
    
    @code {
    		private void NavigateHandler(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);
    	                }
    	            }
    		}
    }
    

三、锚点导航

锚点导航可以导航到指定组件上具有指定Id属性的元素上,与<a>标签的用法类似。

  • 锚点路径语法:"/routeName#id"
  • 注意锚点路径使用#符号将组件路由路径与元素Id拼接一起,前缀的/可有可无,但是routeName必须要写上,即使只是锚点到当前组件上的某个元素,也必须写上,否则会导航到基URI的锚点上,例如"#id"会导航到http://localhost:5242/#title
  • 锚点在当前组件上时,不会触发增强型导航也不会刷新页面,只是在本页面上跳转。锚点在其他组件上,默认则触发增强型导航。

目前可以使用<a>标签、NavLink组件和NavigateTo方法进行锚点导航:

  • <a href="/counter#targetElement">

  • <NavLink href="/counter#targetElement">

  • Navigation.NavigateTo("/counter#targetElement")

  • 示例

    @page "/test"
    
    <h1 id="title">Counter</h1>
    <a href="/counter#title">锚点</a>
    

四、NavigationLock

内置组件<NavigationLock>的作用是锁定当前组件的导航,从而进行相关的业务处理。

事件处理程序

<NavigationLock>组件提供OnBeforeInternalNavigation事件,当使用了<NavigationLock>的组件的所在页面导航到其他页面前触发。

  • 这里的导航,包括点击指向内部URI的链接以及调用NavigateTo()方法(不论是不是增强型导航),但不包括直接通过浏览器地址栏输入进行的访问以及点击指向外部URI的链接。

  • OnBeforeInternalNavigation事件接收一个LocationChangingContext类型对象参数,与前文中通过RegisterLocationChangingHandler()方法注册的处理程序接收的参数一样

  • 示例

    @page "/naviLockTest"
    @inject NavigationManager Navigation
    @inject IJSRuntime JSRuntime
    
    <NavigationLock OnBeforeInternalNavigation="OnBeforeInternalNavigation"/>
    
    <h3>NaviLockTest</h3>
    <button @onclick="NaviToHome">首页</button>
    
    <a href="https://www.baidu.com">百度</a>
    
    @code {
        private void NaviToHome(MouseEventArgs e)
        {
            Navigation.NavigateTo("/");
        }
    
        private async Task OnBeforeInternalNavigation(LocationChangingContext context)
        {
            //confirm是Javascript的内置方法,这里通过js让浏览器跳出提示框,并返回选择结果
            var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm", "确认离开?");
            if (!isConfirmed)
            {
                context.PreventNavigation();
            }
        }
    }
    

在这里插入图片描述

外部跳转提示框

<NavigationLock>组件提供ConfirmExternalNavigation属性,用于设置当导航到外部URI时,是否跳出提示框。

  • 这里的导航,指的是点击指向外部URI的链接,这种情况下不会触发OnBeforeInternalNavigation事件,可以当作互补。

  • 示例

    @page "/naviLockTest"
    @inject NavigationManager Navigation
    @inject IJSRuntime JSRuntime
    
    <NavigationLock ConfirmExternalNavigation="true" OnBeforeInternalNavigation="OnBeforeInternalNavigation"/>
    
    <h3>NaviLockTest</h3>
    <button @onclick="NaviToHome">首页</button>
    
    <a href="https://www.baidu.com">百度</a>
    
    @code {
        private void NaviToHome(MouseEventArgs e)
        {
            Navigation.NavigateTo("/");
        }
    
        private async Task OnBeforeInternalNavigation(LocationChangingContext context)
        {
            //confirm是Javascript的内置方法,这里通过js让浏览器跳出提示框,并返回选择结果
            var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm", "确认离开?");
            if (!isConfirmed)
            {
                context.PreventNavigation();
            }
        }
    }
    

在这里插入图片描述

五、NavLink

内置组件<NavLink>的用法与<a>标签的用法是类似的(其本质就是<a>元素),只不过NavLink组件会根据其 href 属性指定的路由与当前页面的 URI 的匹配情况,自动添加或移除CSS 类 active 。使得开发人员能够轻松地为当前活动的链接设置特定的样式,从而提供给用户清晰的导航反馈。

  • 简单的说,<NavLink>就是一个会自动添加或移除CSS 类 active<a>元素

常用属性

href:设定导航的URL。

target:是否在新的页签中打开,可以设置为target="_blank”表示在浏览器中新建一个新的空白标签页打开当前链接

Match:活动样式的匹配模式,有效值为NavLinkMatch.AllNavLinkMatch.Prefix(默认),前者表示href与当前页面的URL完全匹配时才处于活动状态;后者表示只要href的值与当前URL的任何前缀匹配即可(例如/component/component/another-segment为匹配)。

ActiveClass:设置NavLink元素的活动类名称,默认情况下活动类名为active,可以通过此属性另行设置(具体可以参考Blazor模板中的导航)

  • 示例
    • NaviLink.razor

      @page "/navi-link"
      <h3>NaviLink</h3>
      
      <div class="nav-item">
          <NavLink href="navi-link" Match="NavLinkMatch.All">
              NaviLink
          </NavLink>
      </div>
      
    • NaviLink.razor.css

      .nav-item ::deep a.active {
          background-color: rgba(255,255,255,0.37);
          color: orange;
      }
      

在这里插入图片描述

增强型导航(内部导航)

一、理解增强型导航

1、Fetch API

Fetch API 是一种用于在浏览器中进行网络请求的现代JavaScript接口。它提供了一种更加灵活和强大的方式来替代传统的XHR(XMLHttpRequest)对象,主要用于发起网络请求并处理响应。Fetch API 可以视为 Ajax 的现代替代品。

Fetch API 的特点如下:

  • 基于 Promise,与传统的回调方式不同,Fetch API 使用 Promise,使得代码更加简洁和易于维护
  • Ajax 技术主要依赖于 XHR(XMLHttpRequest)对象来处理所有的网络请求,而 Fetch API 则通过将多个不同的接口分散到不同的对象中来执行网络请求,从而提供了更灵活和直观的用法

在 Blazor Web App 中,增强型导航和增强型表单都使用了Fetch API 技术。

2、增强型导航

非增强型导航中,也就是正常的网页访问,浏览器会向服务器发送请求并加载整个页面。重点在于加载整个页面,不管发生发起何种访问请求,非增强型导航总会将整个页面重新加载。

而如果是增强性导航,则在导航栏中单击某个页面的链接时,会在服务器上将已经渲染的页面与将要渲染的页面在HTML DOM中进行对比,将不同之处修补到现有HTML DOM 中,从而实现局部的UI更新。

Blazor 增强性导航的工作原理如下:

  • 当在 Blazor 应用内部访问某个 Razor 组件时,框架会拦截该请求,判断其是否为内部导航(即基 URI 相同)。如果是非 Blazor 导航(例如从外部链接访问),则会执行整页重载
  • 如果请求被判定为 Blazor 内部导航,框架会将该请求转换为 fetch 请求,以便进行异步内容加载
  • 之后,框架会将需要更新的部分内容修补到 HTML DOM 中,这个修补过程包括向 DOM 中添加、更新或删除元素和内容

利用 Fetch API,Blazor 实现了页面的局部更新,而不重新绘制整个页面,从而加快响应速度,提高用户体验

二、禁用增强型导航

全局禁用

要全局禁用增强型导航和表单处理,请将 Blazor.startdisableDomPreservation 设置为 true

  • 在App.razor组件下,找到js脚本引入位置,进行修改

    <script src="_framework/blazor.web.js" autostart="false"></script>
    <script>
        Blazor.start({
            ssr: { disableDomPreservation: true }
        });
    </script>
    

单元素禁用

默认情况下,Blazor框架已经启用增强型导航,但可使用 data-enhance-nav="false" 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>
    

三、增强的表单处理的启用与禁用

启用增强型表单

EditForm组件的Enhance组件参数或 HTML 的<form>表单的data-enhance属性可以设置对表单的 POST 请求采用增强型导航。

  • 增强型表单的使用仅适用于 Blazor 终结点,将增强型表单在非 Blazor 终结点(例如传统MVC/Razor Page等)中使用会导致错误

  • 示例

    <EditForm ... Enhance ...>
        ...
    </EditForm>
    
    <form ... data-enhance ...>
        ...
    </form>
    

禁用增强型表单

  • 对于EditForm组件,禁用增强型表单,只需要将 Enhance 组件参数移除或将其设置为Enhance="false"
  • 对于 HTML <form> 表单,禁用增强型表单,只需要将 data-enhance 属性移除或将其设置为data-enhance="false"
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SchuylerEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值