详解vue-router中的导航守卫

3,236 阅读5分钟

导航守卫是什么

按照官方文档说明,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。

vue-router 中,导航守卫是一种非常重要的功能,它允许你在路由导航过程中添加逻辑验证和处理,比如判断用户是否登录,如果没有登录就跳转到登录页面,如果已经登录就跳转到首页。

导航守卫的分类

Vue Router 中,有三种类型的导航守卫方法可以使用:

  • 全局守卫:beforeEachafterEach

  • 独享路由守卫:beforeEnter

  • 组件内路由守卫:beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave

全局守卫

全局守卫就是在路由跳转时,对整个应用内的所有路由进行拦截,然后进行一些操作。

全局守卫分为全局前置守卫全局后置守卫

全局前置守卫就是在路由跳转之前进行拦截,全局后置守卫就是在路由跳转之后进行拦截。

全局前置守卫的使用方法如下:

router.beforeEach((to, from, next) => {
  // ...
})

全局后置守卫的使用方法如下:

router.afterEach((to, from) => {
  // ...
})

路由独享守卫

路由独享守卫就是在路由跳转时,对应用内的某个特定的路由进行拦截,然后进行一些操作。

路由独享守卫的使用方法如下:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内守卫

组件内守卫就是在组件内部定义的特殊守卫方法,用于处理组件级别的路由导航。常见的组件内守卫包括:

  • beforeRouteEnter: 在路由进入组件之前被调用,可以访问不到组件实例(this),但可以通过回调函数获取组件实例。

  • beforeRouteUpdate: 在当前路由被复用时调用,例如同一个组件在不同参数下进行切换时。

  • beforeRouteLeave: 在离开当前路由之前调用,可以询问用户是否保存未保存的数据或执行其他清理操作。

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

导航守卫的参数

导航守卫的参数如下:

  • to: 即将要进入的目标路由对象,包含了目标路由的路径、参数、查询参数等信息,可以用来判断要导航到哪个路由。

  • from: 当前导航正要离开的路由,包含了当前路由的路径、参数、查询参数等信息,可以用来判断当前路由的状态。

  • next 方法:用于控制导航行为。它有以下几种用法:

    • next(): 允许导航,继续进行下一步导航操作。
    • next(false): 取消导航,终止当前导航操作。
    • next('/')next({ path: '/' }): 重定向到指定的路径。
    • next(error): 导航出错,可以传递一个错误对象给 next 方法。

导航守卫的执行顺序

导航守卫的执行顺序如下:

全局前置守卫 -> 路由独享守卫 -> 组件内守卫 -> 全局后置守卫

导航守卫的使用场景

全局前置守卫

  • 判断用户是否登录,如果没有登录就跳转到登录页面,如果已经登录就跳转到首页。
router.beforeEach((to, from, next) => {
  if (to.path === '/login') {
    next()
  } else {
    const token = localStorage.getItem('token')
    if (!token) {
      next('/login')
    } else {
      next()
    }
  }
})

全局后置守卫

  • 记录用户访问的页面。
router.afterEach((to, from) => {
  localStorage.setItem('path', to.path)
})

路由独享守卫

  • 判断用户是否有权限访问某个页面,如果没有权限就跳转到首页。
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        const token = localStorage.getItem('token')
        if (!token) {
          next('/')
        } else {
          const role = localStorage.getItem('role')
          if (to.meta.role && to.meta.role.indexOf(role) === -1) {
            next('/')
          } else {
            next()
          }
        }
      }
    }
  ]
})

组件内守卫

  • 数据预加载:在进入组件之前,需要先加载一些数据。可以使用 beforeRouteEnter 组件内守卫来调用接口获取数据,并在数据加载完成后再进入组件。
const Foo = {
  beforeRouteEnter(to, from, next) {
    fetchData().then(data => {
      next(vm => {
        vm.data = data; // 将数据传递给组件实例
      });
    });
  },
};
  • 路由参数更新:当同一个组件在不同参数下进行切换时,可能需要根据新的参数更新组件的数据或状态。可以使用 beforeRouteUpdate 组件内守卫来处理这种情况。
const Baz = {
  beforeRouteUpdate(to, from, next) {
    if (to.params.id !== from.params.id) {
      // 当路由参数 id 发生变化时,重新请求数据
      this.fetchData(to.params.id);
    }
    next();
  },
};
  • 数据清理:在离开当前路由之前需要执行一些清理操作,例如取消订阅事件、重置组件状态等。可以使用 beforeRouteLeave 组件内守卫来处理这些操作。
const Bar = {
  beforeRouteLeave(to, from, next) {
    if (this.hasUnsavedChanges()) {
      if (confirm('是否保存修改的数据?')) {
        this.saveData(); // 保存修改的数据
      }
    }
    next(); 
};
  • 页面切换动画:在切换页面时添加过渡动画效果,以提升用户体验。可以在 beforeRouteEnterbeforeRouteLeave 组件内守卫中设置过渡动画的相关逻辑。
const Qux = {
  beforeRouteEnter(to, from, next) {
    // 在进入组件之前设置初始过渡状态
    this.transitionName = 'slide-in';
    next();
  },
  beforeRouteLeave(to, from, next) {
    // 在离开组件之前设置过渡状态
    this.transitionName = 'slide-out';
    next();
  },
};

参考资料