首页 > 其他分享 >Blazor和Vue对比学习(进阶.路由导航五):路由守卫

Blazor和Vue对比学习(进阶.路由导航五):路由守卫

时间:2022-12-15 23:56:32浏览次数:54  
标签:Vue 进阶 LocationChanged 守卫 context 组件 导航 路由

路由守卫,可以认为是设置在导航源和目标之间的中间件。Vue在代码上,表现为命名约定的钩子(类似于生命周期钩子),而Blazor会更复杂一些。Vue Router的路由守卫功能非常完善,而Blazor则相对简陋。同时,Blazor的路由守卫需要结合生命周期函数和事件,使用起来反而更加复杂。

 

 

一、Vue Router的路由守卫

1、Vue Router的路由守卫,大致可以分为三类。它们的调用顺序,如下图所示:

  • 全局守卫,所有导航都会经过,有三个钩子,分别为beforeEach(全局前置守卫)、beforeResove(全局解析守卫)、afterEach(全局后置守卫)
  • 路由守卫,在路由文件的路由route中定义的守卫,导航至宿主路由时会经过,只有一个钩子,beforeEnter(进入路由前)
  • 组件守卫,在组件中定义的钩子,有两个,失活组件中,经过beforeRouteEnter(进入组件前);激活组件中,经过beforeRouteLeave/onBeforeRouteLeave(离开组件前)。
  • 特殊:还有一个组件守卫,但在同组件不同路径间导航时,如路由/user/:id,从/user/1导航到/user/2时,经过beforeRouteUpdate/onBeforeRouteUpdate(组件更新前)

 

 

 

  2、基本使用

(1)在路由文件Router/index.js中,设置全局守卫和路由独享守卫

import { createRouter, createWebHistory } from 'vue-router'
//路由=========================================================================================================
const routes = [
  {
    path: '/',
    name: 'index', 
    component: ()=> import('../views/Index.vue'),
  },
  {
    path: '/student',
    name: 'student', 
    meta: { requiresAuth: true }, //meta,称之为元信息,可以标注一些路由的特性,本质上就是一些标记
    component: ()=> import('../views/Student.vue'),
  },
  {
    path: '/student-detail',
    name: 'student-detail', 
    component: ()=> import('../views/StudentDetail.vue'),
    //(3)路由独享守卫***************************************************
    //只在进入路由时触发,如从/student-detail/1,导航到/student-detail/2时,不会调用
    beforeEnter: (to,from)=>{
      console.log("路由独享守卫")
    }
  }
]

//路由器========================================================================================================
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

//全局守卫=======================================================================================================
//(2)全局前置守卫******************************************************* router.beforeEach((to,from)=>{ //①to为目标路由,from为源路由,可以调用name、path、params、query、meta等信息,如to.meta,from.name等 //②如果返回值为true或undefine,则进入下一个路由守卫;如返回false,则导航停止 //③如返回一个路由对象,可以设置导航转向,如【return {name:"index"}】 console.log("全局前置守卫") return true /*④路由权限,可以在全局前置守卫进行,如下所示 if(to.meta.requiresAuth && to.name !== 'Login'){ return {name:'Login'} } */ }) //(5)全局解析守卫******************************************************* router.beforeResolve((to,from)=>{ //此时,组件守卫、路由守卫和异步路由均已经解析完成 console.log("全局解析守卫") return true }) //(6)全局后置守卫(钩子)************************************************* router.afterEach((to, from, failure) => { //严格来说,不能称为守卫,因为此时导航已经完成,无法对导航进行干预 //此时可以完成更改页面标题等辅助功能 console.log("全局后置钩子") }) export default router

 

(2)在组件中设置组件守卫

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router';

/*(4)组件守卫(进入激活组件前)
 *如果不是组合式API,还可以调用beforeRouteEnter。
 *不能获取组件实例this!因为当守卫执行时,组件实例还没被创建 
beforeRouteEnter((to, from)=>{
  console.log("组件守卫,进入组件前")
})
*/

//(*)组件守卫(组件更新前)
// 在当前路由改变,但是该组件被复用时调用。如路径/users/:id,在/users/1和/users/2之间跳转的时候
// 在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例this
onBeforeRouteUpdate((to,from)=>{
  console.log("组件守卫,组件更新前")
})

//(1)组件守卫(离开失活组件前)
// 在导航离开渲染该组件的对应路由时调用
// 与beforeRouteUpdate一样,它可以访问组件实例this
onBeforeRouteLeave((to,from)=>{
  console.log("组件守卫,离开组件前")
})
</script>

<template>
    <h1>这里是关于首页Index.vue</h1>
</template>

 

 

 

二、Blazor的路由守卫

1、Blozor中没有明确提出路由守卫的概念,但可以从路由守卫的角度去理解。目前Blazor提供的路由守卫很简单,主要划分三类:

  • 全局路由守卫:OnNavigateAsync,当新导航发生时执行的回调。
  • 组件路由守卫:由NavigationManager提供,一个是导航正在离开RegisterLocationChangingHandler(方法,参数是一个Func委托),一个是导航已经离开LocationChanged(事件)。
  • 特殊路由组件:<Navigating>:导航等待期间的内容;<NavigationLock>:可以强制导航离开时进行弹窗确认。
  • 实际上只有三个守卫,执行的顺序为:RegisterLocationChangingHandler > OnNavigateAsync > LocationChanged

 

2、基本使用:

(1)全局路由守卫OnNavigateAsync,在App.razor根组件中定义

@inject NavigationManager Navigation
<Router AppAssembly="@typeof(App).Assembly" OnNavigateAsync="@OnNavigateAsync">
    ......
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        Console.WriteLine("全局守卫"+context.Path);
//可以进行路由转向 //Navigation.NavigateTo("/student"); /*OnNavigateAsync本质上是导航时执行的一个回调,如果在这个回调中执行异步方法,应该在方法中传入context的CancellationToken属性 *如导航到/about,但在异步方法PostAsJsonAsync还在执行时,我们又快速改变了导航地址,此时PostAsJsonAsync不应再执行 *所以传入context.CancellationToken,导航异常变化时,会将CancellationToken的IsCancellationRequest设置为true,取消异步方法 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 = [345, 789, 135, 689]; foreach (var productId in productIds) { context.CancellationToken.ThrowIfCancellationRequested(); Products.Prefetch(productId); } } */ } }

 

(2)组件路由守卫RegisterLocationChangingHandler和LocationChanged,在组件中定义

 

//RegisterLocationChangingHandler是非托管资源,LocationChanged是事件,两者在组件销毁时,都要手工释放内存
//所以组件必须实现IDisposable接口,然后在生命周期函数Dispose()中释放资源
@page "/"
@implements IDisposable
@inject NavigationManager Navigation
<PageTitle>首页</PageTitle>

@code{
    //RegisterLocationChangingHandler,导航正在发生前===========================================================
    //在生命周期函数OnAfterRender中,调用Navigation.RegisterLocationChangingHandler方法,方法参数为守卫方法
    //Navigation.RegisterLocationChangingHandler方法的返回值类型为IDisposable,在组件销毁时,调用Dispose()销毁
    //此时导航还未实际发生,可以进行导航停止、转向等操作
    private IDisposable? registration;
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            registration = Navigation.RegisterLocationChangingHandler(OnLocationChanging);
        }
    }
    private ValueTask OnLocationChanging(LocationChangingContext context)
    {
        Console.WriteLine("组件守卫:OnLocationChanging导航正在发生前");
        Console.WriteLine(context.TargetLocation); //目标地址
        Console.WriteLine(context.HistoryEntryState); //目标地址关联的历史记录状态
        Console.WriteLine(context.IsNavigationIntercepted); //是否从链接截获了导航

        if (context.TargetLocation.Contains("student"))
        {
            context.PreventNavigation(); //阻止导航
        }
        return ValueTask.CompletedTask;
    }

    //LocationChanged,导航已经发生=============================================================================
    //在生命周期函数OnInitialized中,订阅Navigation.LocationChanged事件,事件处理函数为守卫方法
    //在组件销毁时,在组件生命周期方法中,移除事件订阅
    //此时导航已经发生,可以导航转向操作,但不能停止导航
    protected override void OnInitialized()
    {
        Navigation.LocationChanged += LocationChanged;
    }
    private void LocationChanged(object? sender, LocationChangedEventArgs args)
    {
        Console.WriteLine("组件守卫:LocationChanged导航已经发生");
        Console.WriteLine(args.Location);//目标地址
        Console.WriteLine(args.HistoryEntryState);//目标地址关联的历史记录状态
        Console.WriteLine(args.IsNavigationIntercepted);//是否从链接截获了导航

    }

    //在生命周期函数Dispose中,移除订阅的事件,并销毁非托管资源registration===========================================
    public void Dispose()
    {
        Navigation.LocationChanged -= LocationChanged;
        registration?.Dispose();
    }
}

 

 

 

(3)<Navigating>:导航等待期间的显示内容,在App.razor根组件中定义

 

<Router AppAssembly="@typeof(App).Assembly" OnNavigateAsync="@OnNavigateAsync">
    ......
    <Navigating>
        <h1>正在导航中......</h1>
    </Navigating>
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        await Task.Delay(2000);//通过在全局守卫中延迟2秒,来测试<Navigating>的功能
    }
}

 

 

(4)<NavigationLock>:强制导航离开时进行弹窗确认,在组件中定义

 

@page "/"
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation

//ConfirmExternalNavigation属性,确定当导航到外部地址时的行为,如为true,则会弹窗提示;如为false,则不会弹窗提示,直接导航
//ConfirmExternalNavigation属性,无论是true或false,导航到内部地址时,都会弹窗提示
<NavigationLock ConfirmExternalNavigation="true" OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
<button @onclick="Navigate">Navigate</button>
<a href="https://www.microsoft.com">Microsoft homepage</a>

@code {
    private void Navigate()
    {
        Navigation.NavigateTo("/student");
    }
    private async Task OnBeforeInternalNavigation(LocationChangingContext context)
    {
        //直接通过JS交互,调用window对象的confirm弹窗方法
        //OnBeforeInternalNavigation回调方法,LocationChangingContext上下文,这个对象和RegisterLocationChangingHandler中的一样
        //如果用户在弹窗中选择取消,则调用LocationChangingContext的PreventNavigation方法,停止导航
        var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm","确定要离开吗?"); 
        if (!isConfirmed)
        {
            context.PreventNavigation();
        }
    }
}

 

标签:Vue,进阶,LocationChanged,守卫,context,组件,导航,路由
From: https://www.cnblogs.com/functionMC/p/16950104.html

相关文章