Vue3中的导航守卫

4.14.5 导航守卫

总共7个守卫,3个全局守卫:全局前置守卫beforeEach、路由前置解析守卫beforeResolve、全局后置守卫afterEach,1个路由独享守卫:进入任一路由前守卫beforeEnter,3个组件内导航守卫钩子:进入新目标前守卫beforeRouteEnter、路由更新前守卫beforeRouteUpdate、离开当前实例前守卫beforeRouteLeave

4.14.5.1 完整的导航解析流程——守卫触发时机

具体的Vue router导航解析流程如下图所示,

  1. 导航被触发:用户点击链接或使用编程式导航触发路由变化。
  2. 失活的组件里调用路由离开当前实例前守卫 beforeRouteLeave
  3. 调用全局的进入任一路由前守卫 beforeEach
  4. 重用的组件里调用路由更新前守卫(vue2.2+) beforeRouteUpdate
  5. 在路由配置里调用进入不同路由前守卫 beforeEnter
  6. 解析异步路由组件。
  7. 被激活的组件里调用进入新的目标实例前守卫 beforeRouteEnter
  8. 调用全局的解析任一路由前守卫(vue2.5+) beforeResolve
  9. 导航被确认。
  10. 调用全局的路由钩子afterEach
  11. 触发DOM更新。
  12. 调用进入新目标前守卫beforeRouteEnter中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
无匹配
有匹配
守卫失败
守卫通过
全局守卫失败
全局守卫通过
解析失败
解析成功
开始
用户触发导航
Vue Router 接收导航事件
查找匹配的路由
显示404页面
检查路由守卫 beforeEnter
导航被取消或重定向
调用全局前置守卫 beforeEach
调用路由组件 beforeRouteEnter
调用路由组件 beforeRouteUpdate
调用路由组件 beforeRouteLeave
调用全局解析守卫 beforeResolve
调用全局后置钩子 afterEach
渲染组件
导航完成

该流程图展示了从用户触发导航开始,到最终渲染组件的整个流程。以下是一些关键步骤的简要说明:

  • 用户触发导航:用户点击链接或使用编程式导航触发路由变化。
  • Vue Router 接收导航事件:Vue Router 监听到导航事件,并开始处理。
  • 查找匹配的路由记录:Vue Router 查找配置中与目标路径匹配的路由记录。
  • 检查路由守卫:如果找到匹配的路由,Vue Router 会检查路由守卫(beforeEnterbeforeEach)。
  • 执行路由组件的生命周期钩子:在路由守卫通过后,Vue Router 会执行组件的导航守卫钩子(beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave)。
  • 执行全局守卫:Vue Router 还会执行全局前置守卫(beforeEach)和解析守卫(beforeResolve),以及全局后置钩子(afterEach)。
  • 渲染组件:所有守卫都通过后,Vue Router 会渲染对应的组件。

请注意,这个流程图是一个简化的版本,实际的 Vue Router 导航解析流程可能更复杂,涉及到更多的细节和配置选项。

概念理解:

失活组件,即将转换到后台不直接面向用户的组件(页面);

被激活组件,即将转换到前台而直接页面向用户的组件(页面);

重用的组件,在Vue中,"重用的组件"通常指的是在多个不同的路由视图中被复用的组件。Vue 2.2+版本引入了beforeRouteUpdate这个路由守卫,它允许开发者在路由参数发生变化时,重新渲染当前组件之前执行一些逻辑。

具体来说,当你使用Vue Router时,每个路由可以关联一个或多个组件。如果这些组件在不同的路由中被复用,那么它们就被称为“重用的组件”。例如,你可能有一个用户信息组件,它在多个页面上显示用户信息,而这些页面可能对应不同的路由。

beforeRouteUpdate守卫是在路由对象上定义的,它在两个条件满足时触发:

  1. 路由的路径保持不变,但是查询参数或hash发生变化。
  2. 路由的路径发生变化,但是当前组件被重用(即组件实例保持活动状态)。

beforeRouteUpdate中,你可以访问到tofrom路由对象,它们分别代表目标路由和当前路由。这个守卫可以用来处理路由变化时的逻辑,例如,根据新的路由参数更新组件的状态。

举例来说,当目标参数中的id发生变化时,调用路由更新前守卫:

    export default {
      watch: {
        '$route'(to, from) {
          // 对路由变化作出响应...
        }
      },
      beforeRouteUpdate(to, from, next) {
        // 在这里可以访问到 to 和 from 路由对象
        // 可以执行一些逻辑,比如更新组件状态
        this.fetchData(to.params.id);
        next();
      },
      methods: {
        fetchData(id) {
          // 根据id获取数据
        }
      }
    }

在这个例子中,fetchData方法会在路由更新时被调用,以确保组件显示的数据是最新的。

以下逐个守卫举例说明。

4.14.5.2 组件内导航守卫钩子
    const UserDetails = {
      template: `...`,
      beforeRouteEnter(to, from) {
        // 在渲染该组件的对应路由被验证前调用
        // 不能获取组件实例 `this` !
        // 因为当守卫执行时,组件实例还没被创建!
        next(vm => {
            // 可通过 `vm` 访问组件实例
        })
      },
      beforeRouteUpdate(to, from) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
        // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
        // just use `this`
        this.name = to.params.name
      },
      beforeRouteLeave(to, from) {
        // 在导航离开渲染该组件的对应路由时调用
        // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
        const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
        if (!answer) return false
      },
    }

组合式API中,通过onBeforeRouteUpdateonBeforeRouteLeave 两个函数分别添加 update 和 leave 守卫。

4.14.5.3 全局前置守卫:beforeEach

当一个导航触发时,全局前置守卫beforeEach按照路由创建顺序调用。该守卫是异步解析执行,此时导航在所有前置守卫解析完成之前一直处于等待中

    const router = createRouter({ ... })
    //参数to:正要进入的目标路由,from:正要离开的原路由
    router.beforeEach((to, from) => {
      // ...
      // 返回false以取消导航,重置回到from的路由;
      return false
      // 返回路由地址,则重定向到该地址,比如用户登录认证失败或用户登录超时等
      //return {name: 'Login'}
      // 返回undefined或true,导航仍有效,正常执行下一个导航守卫
    })
4.14.5.4 全局导航解析守卫:beforeResovle

解析守卫beforeResovle在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。与beforeEnter类似,在每次导航时都会触发。

    router.beforeResolve(async to => {
      if (to.meta.requiresCamera) {
        try {
          await askForCameraPermission()
        } catch (error) {
          if (error instanceof NotAllowedError) {
            // ... 处理错误,然后取消导航
            return false
          } else {
            // 意料之外的错误,取消导航并把错误传给全局处理器
            throw error
          }
        }
      }
    })

router.beforeResolve 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。

4.14.5.5 全局后置守卫:afterEach

afterEach也称全局后置钩子,它不会接受 next 函数也不会改变导航本身,其主要用于分析、更改页面标题、声明页面等辅助功能。

    // failure导航失败对象
    router.afterEach((to, from, failure) => {
      if (!failure) sendToAnalytics(to.fullPath)
    })
4.14.5.6 导航守卫中inject注入provide全局状态数据

从Vue3.3开始,可以在导航守卫内使用inject()方法。在app.provide()中提供的所有内容都可以在 router.beforeEach()router.beforeResolve()router.afterEach() 内获取到:

    // main.js
    const app = createApp(App)
    app.provide('global', 'hello injections')
    
    // router.ts or main.ts
    router.beforeEach((to, from) => {
      const global = inject('global') // 'hello injections'
      // a pinia store
      const userStore = useAuthStore()
      // ...
    })
4.14.5.7 路由独享守卫:beforeEnter

beforeEnter 守卫只在进入不同路由时触发,不会在 paramsqueryhash 改变时触发。即只有在从一个不同的路由导航时,才会被触发。

    const routes = [
      {
        path: '/users/:id',
        component: UserDetails,
        beforeEnter: (to, from) => {
          // reject the navigation
          return false
        },
      },
    ]

将一个函数数组传递给 beforeEnter,为不同的路由重用守卫时做适当的清理工作。也可以通过使用路径meta字段和全局导航守卫来实现类似的行为。

    function removeQueryParams(to) {
      if (Object.keys(to.query).length)
        return { path: to.path, query: {}, hash: to.hash }
    }
    
    function removeHash(to) {
      if (to.hash) return { path: to.path, query: to.query, hash: '' }
    }
    
    const routes = [
      {
        path: '/users/:id',
        component: UserDetails,
        beforeEnter: [removeQueryParams, removeHash],
      },
      {
        path: '/about',
        component: UserDetails,
        beforeEnter: [removeQueryParams],
      },
    ]

从以上示例中可以看出,beforeRouteUpdate、beforeRouteLeave和beforeRouteEnter属于组件实例的生命周期钩子函数,beforeEach、beforeResovle和afterEach属于路由实例钩子函数,而beforeEnter属具体的路由属性配置回调。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南腔北调-pilmar

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

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

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

打赏作者

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

抵扣说明:

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

余额充值