likes
comments
collection
share

Vue-Router 第十节:导航守卫

作者站长头像
站长
· 阅读数 31

Vue-Router 第十节:导航守卫

导航守卫是 Vue Router 提供的一种机制,用于在路由发生改变时执行一些特定的逻辑。它们允许你在路由发生变化前、变化后或路由组件内部执行一些操作,比如检查用户是否登录、获取数据、验证路由参数等。

一、全局前置守卫 (beforeEach)

1、概念

全局前置守卫 beforeEach 在路由改变之前被调用。

// 创建路由实例
const router = createRouter({ 
    history: createWebHistory(),
    routes
})
router.beforeEach((to, from, next) => {
     // 处理逻辑
})

2、参数

  • to :即将进入的路由对象

  • from:当前导航正要离开的路由

  • next : 一个函数,必须调用该方法来解析这个钩子,它用于决定导航的下一步行为。

    next()详解:

    next() :当调用 next() 而不带任何参数时,导航将继续进行到下一个守卫。如果当前守卫是最后一个守卫(即没有后续的守卫或组件内守卫),那么导航将确认并触发组件的更新。

    next(false) :调用 next(false) 将中断当前的导航。如果浏览器的 URL 改变了(通常是由于用户手动点击了一个链接),那么 URL 将会被重置到 from 路由对应的地址。

    next('/path')next({ path: '/path' }) :调用 next 并传入一个路径字符串或路由对象将导航到一个新的路由。这类似于用户点击了一个链接到该路径的 <router-link>。当前的导航将被中断,并启动一个新的导航到指定的路径。

    next(to) :这与传入路径或路由对象的效果相同,to 是即将进入的目标路由对象。

    next(error) :调用 next 并传入一个错误对象将导航中断,并把这个错误传递给路由的错误处理函数。

3、使用场景

常用来检查用户是否登录、检查用户权限、加载数据等。

4、案例

检查用户是否登录为案例,本案例十分简单粗糙.........但是对我们要学的内容是能体现出来的。

先模拟一个登录界面:

Vue-Router 第十节:导航守卫

// 页面login.vue
<template>
    <div class="login">
        <el-card style="max-width: 480px">
            <el-form :inline="true" :model="formInline" class="demo-form-inline">
                <el-form-item label="账号:">
                    <el-input v-model="formInline.user" placeholder="请输入账号" />
                </el-form-item>
                <el-form-item label="密码:">
                    <el-input type='password' v-model="formInline.password" placeholder="请输入密码" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" style="margin-top: 10px; background-color: cadetblue" @click="onSubmit">登陆</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { useRouter } from 'vue-router'

const formInline = reactive({
    user: '',
    password: ''
})

const rouer = useRouter()

const onSubmit = () => {
    //******重点1*********登录以后给localStorage添加token; 业务代码、功能代码自己写这里是极简。
    localStorage.setItem('token', '1')      
    rouer.push({path:'/home'})          // 登录成功 跳转home路由
}

</script>
<style scoped>
.login {
    width: 300px;
    height: 100%;
    background-color: azure;
}

.demo-form-inline .el-input {
    --el-input-width: 220px;
}

.demo-form-inline .el-select {
    --el-select-width: 220px;
}
</style>

配置路由及添加路由前置守卫

// router/index.vue
import path from 'path';
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
// 1、定义路由 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
    // 可直接导入或者引入
    {
        path: '/',
        component: () => import("@/views/login/index.vue"),
    },
    {
        path: '/home',
        component: ()=>import("@/views/home/index.vue")
    }
]

// 2、创建路由实例并传递routes
const router = createRouter({
    history: createWebHistory(),
    routes
})
// **************************重点在这请看过来*****************
const whileList = ['/']                       // 创建一个白名单存放不受判断的路由,这里是我们的登录页面。
router.beforeEach((to, form, next) => {    
    // 如果存在白名单里,或者token存在那就说明已经登陆了不用再登录了!
    if (whileList.includes(to.path) || localStorage.getItem('token')) {
        next()  // 正常进行
    } else {
        next('/')   // 跳转至登录界面
    }
})

// 导出
export default router

Vue-Router 第十节:导航守卫

Vue-Router 第十节:导航守卫

进行测试:现在可以将此网页url复制,关掉当前页,在新的窗口打开,效果是:会直接进入home页面。 此时,再把本地缓存的token清掉,再刷新页面 就会进入登陆页面。

Vue-Router 第十节:导航守卫

二、全局解析守卫(beforeResolve)

1、概念

解析守卫(beforeResolve)在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。

// 创建路由实例
const router = createRouter({ 
    history: createWebHistory(),
    routes
})
router.beforeResolve((to, from, next) => {  
      // 获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。 
  next()  
})

2、参数

  • to :即将进入的路由对象
  • from:当前导航正要离开的路由
  • next : 一个函数,必须调用该方法来解析这个钩子,它用于决定导航的下一步行为。

3、使用场景

(1)用户权限验证

(2)登录检查

(3)全局配置(你可以在守卫中检查当前环境,并根据需要设置全局配置,如API接口的基本URL或请求头等。)

(4)数据获取(在用户访问某个需要加载特定数据的页面之前,全局解析守卫可以用来请求这些数据,并在页面加载时直接使用。这有助于优化用户体验,减少页面加载后的数据获取延迟。)

4、案例

例子与前置案例相似,就不展示啦,可以自己试试。

三、全局后置守卫(afterEach)

1、概念

全局后置守卫afterEach在导航完成后被调用,此时组件已经渲染完成。

// 创建路由实例
const router = createRouter({ 
    history: createWebHistory(),
    routes
})
router.afterEach((to, from) => {  
  // 导航完成后执行的逻辑...  
})

2、参数

  • to :即将进入的路由对象
  • from:当前导航正要离开的路由

注: afterEach不需要传入next()函数, next 函数也不会改变导航本身。

3、使用场景:

全局后置守卫的使用场景主要包括对跳转后的页面进行一些操作,例如滚动条回调至顶部位置、更新页面title、懒加载结束等。它们特别适用于分析、更改页面标题、声明页面等辅助功能,以及其他需要在路由跳转后执行的操作。

注: 全局后置守卫与全局前置守卫不同,它不会改变导航本身,只是简单地执行一些在路由跳转后的操作。因此,在使用全局后置守卫时,需要清楚其执行时机和使用目的,确保在路由跳转后正确地执行所需的操作。

4、案例

// router/index.vue
import path from 'path';
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
// 1、定义路由 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
    // 可直接导入或者引入
    {
        path: '/',
        component: () => import("@/views/login/index.vue"),
    },
    {
        path: '/home',
        component: ()=>import("@/views/home/index.vue")
    }
]

// 2、创建路由实例并传递routes
const router = createRouter({
    history: createWebHistory(),
    routes
})
// **************************重点在这请看过来*****************
router.afterEach((to, form) => {
    console.log('后置')
    alert('跳转啦')
    // 你也可以在这里执行其他需要在路由跳转后进行的操作  
    // 例如,记录用户行为、发送统计信息等  
})

// 导出
export default router

Vue-Router 第十节:导航守卫

四、路由独享的守卫(beforeEnter)

1、概念

在路由配置文件中定义,只对当前路由生效。即:eforeEnter 守卫 只在进入路由时触发,不会在 paramsqueryhash 改变时触发。

const routes: Array<RouteRecordRaw> = [
    // 可直接导入或者引入
    {
        path: '/',
        component: () => import("@/views/login/index.vue"),
    },
    {
        path: '/home',
        component: () => import("@/views/home/index.vue"),
         beforeEnter: (to, from, next) => {         //*************** 路由独享的守卫
        // 逻辑判断...  
        next()  
      }  
    }
]

2、参数

  • to :即将进入的路由对象

  • from:当前导航正要离开的路由

  • next : 一个函数,必须调用该方法来解析这个钩子,它用于决定导航的下一步行为。

    :和全局前置守卫参数一样。

3、使用场景

(1)特定路由的权限验证:在某些应用中,不同的路由可能对应着不同的权限级别。通过路由独享的守卫,开发者可以针对每个路由进行单独的权限验证,确保用户只能访问他们被授权访问的页面。

(2)路由进入前的数据预加载:在进入一个展示用户信息的页面之前,可能需要从服务器获取该用户的详细信息。通过在这个路由的守卫中进行数据请求,可以在页面加载时直接使用这些数据,提高用户体验。

(3)离开路由前的确认或清理操作:用户尝试离开某个路由时,可能需要执行一些确认操作,比如提示用户是否保存了修改的内容。或者,可能需要执行一些清理操作,比如清除该路由相关的本地状态或取消某些异步请求。

4、案例

特定路由的权限验证

// router/index.vue
import About from '@/views/about/index.vue'
import path from 'path';
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
// 1、定义路由 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
    // 可直接导入或者引入
    {
        path: '/',
        component: () => import("@/views/login/index.vue"),
    },
    {
        path: '/home',
        component: () => import("@/views/home/index.vue"),
        beforeEnter:(to,from,next)=>{                          //*****看过来********* home的路由独享守卫
          if(to.query.role==='管理员'){                         // 在独享守卫里判断登陆用户是否是有‘管理员’角色  有就下一步
            next()
          }else{
            window.confirm('非管理员角色不得进入,请登录管理员账号!')             // 没有就不能登录
            next(false)
          }
        }
    }
]

// 2、创建路由实例并传递routes

const router = createRouter({
    // 路由的模式:  vue2:
    // vue2 mode  history | vue3  createWebHistory
    // vue2 mode  hash    | vue3  createWebHashHistory
    // vue2 mode  abstact | vue3  createMemoryHistory   
    history: createWebHistory(),
    routes
})

// 导出
export default router


// 页面login.vue
<template>
    <div class="login">
        <el-card style="max-width: 480px">
            <el-form :inline="true" :model="formInline" class="demo-form-inline">
                <el-form-item label="账号:">
                    <el-input v-model="formInline.user" placeholder="请输入账号" />
                </el-form-item>
                <el-form-item label="密码:">
                    <el-input type='password' v-model="formInline.password" placeholder="请输入密码" />
                </el-form-item>

                <el-form-item>
                    <el-button type="primary" style="margin-top: 10px; background-color: cadetblue" @click="onSubmit">登陆</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { useRouter } from 'vue-router'

const formInline = reactive({
    user: '',
    password: '',
    role:''
})

const rouer = useRouter()

const onSubmit = () => {
    if(formInline.user==='1' &&  formInline.password==='1'){
        formInline.role ='管理员'                                 // 模拟账号密码为 1 1 的用户有管理员角色。 
    }
    console.log('submit!')                                      
    localStorage.setItem('token', '1')
    rouer.push({path:'/home',query:formInline})                  // 跳转路由是加上角色参数 注:query 只接受对象
}

</script>
<style scoped>
.login {
    width: 300px;
    height: 100%;
    background-color: azure;
}


.demo-form-inline .el-input {
    --el-input-width: 220px;
}

.demo-form-inline .el-select {
    --el-select-width: 220px;
}
</style>

Vue-Router 第十节:导航守卫

Vue-Router 第十节:导航守卫

Vue-Router 第十节:导航守卫

Vue-Router 第十节:导航守卫

五、组件内的守卫

1、概念

用于在组件内部路由变化时执行特定的逻辑,这些守卫允许开发者在路由跳转前、跳转后以及组件重用时执行相应的操作。

  • beforeRouteEnter:在渲染该组件的对应路由被验证前调用(在进入路由之前,组件实例还没有被创建,因此不能访问this),可以传一个回调给next来访问组件实例。
  • beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用,例如:对于一个带有动态参数的路径 /users/:id,在 /users/1/users/2 之间跳转的时候,会渲染同样的组件,因此组件实例会被复用,在这个情况下被调用,此时组件已经挂载好了,可以访问组件实例 this
  • beforeRouteLeave: 在导航离开渲染该组件的对应路由时调用,可以访问组件实例 this
 beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },

2、参数

  • to :即将进入的路由对象

  • from:当前导航正要离开的路由

  • next : 一个函数,必须调用该方法来解析这个钩子,它用于决定导航的下一步行为。

    注:和全局前置守卫参数一样。

3、使用场景

beforeRouteEnter:可以通过这个守卫进行诸如权限验证、数据预加载等操作。例如,可以在进入页面之前异步获取数据。

beforeRouteUpdate :在这里根据新的路由参数更新组件状态。

beforeRouteLeave:这个守卫常用于提示用户是否保存了更改,或者执行一些清理工作,如取消未完成的异步请求或清除组件内的本地状态

4、案例

beforeRouteLeave 为例:提示用户是否保存了更改。

// router/index.vue

import About from '@/views/about/index.vue'
import path from 'path';
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
// 1、定义路由 每个路由都需要映射到一个组件。
const routes: Array<RouteRecordRaw> = [
    // 可直接导入或者引入
    {
        path: '/',
        component: () => import("@/views/login/index.vue"),
    },
    {
        path: '/home',
        component: () => import("@/views/home/index.vue")
    }
]

// 2、创建路由实例并传递routes

const router = createRouter({
    // 路由的模式:  vue2:
    // vue2 mode  history | vue3  createWebHistory
    // vue2 mode  hash    | vue3  createWebHashHistory
    // vue2 mode  abstact | vue3  createMemoryHistory   
    history: createWebHistory(),
    routes
})

// 导出
export default router


// home/index.vue
<template>
    <div>
        <H1>
            我是Home页面
        </H1>
        <div>
          <label>姓名:</label> <input style="height: 45px; width: 80%;"  type="text" placeholder="请输入名字" v-model="name">
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'    // vue3使用setup语法会需要用onBeforeRouteLeave 函数

const name =ref<string>('')
const Changes = ref<boolean>(false)

watch(name, () => {                       // 监听name改变           
    if (name) {
        Changes.value = true
    }
})

onBeforeRouteLeave((to, from, next) => {
    if(Changes.value){                         // 如果改变了就去提示
        const answer = window.confirm('您有未保存的更改,确定要离开吗?');
    if (answer) {
        next()
    } else {
        next(false)
    } 
    }else{                                    // 否则正常进入
        next()
    }
})
</script>

<style></style>

Vue-Router 第十节:导航守卫

Vue-Router 第十节:导航守卫

六、完整导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用beforeRouteLeave守卫(导航离开渲染该组件路由时)。
  3. 调用全局的beforeEach守卫(全局前置守卫)。
  4. 在重用的组件里调用beforeRouteUpdate守卫(可以获取组件实例,通常用于组件复用时更新数据)。
  5. 在路由配置里调用beforeEnter守卫(路由独享的守卫)。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用beforeRouteEnter守卫(在渲染该组件的对应路由被验证前调用,此时无法取到组件实例,因为该守卫会在导航确认前被调用,即将登场的新组件还没被创建,它是支持给next()方法传递回调函数的唯一守卫)。
  8. 调用全局的beforeResolve守卫(全局解析守卫)。
  9. 导航被确认。
  10. 调用全局的afterEach钩子(全局后置守卫)。
  11. 触发DOM更新。
  12. 用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数。
转载自:https://juejin.cn/post/7363301791118639145
评论
请登录