导航守卫是什么
按照官方文档说明,vue-router
提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。
在 vue-router
中,导航守卫是一种非常重要的功能,它允许你在路由导航过程中添加逻辑验证和处理,比如判断用户是否登录,如果没有登录就跳转到登录页面,如果已经登录就跳转到首页。
导航守卫的分类
在 Vue Router
中,有三种类型的导航守卫方法可以使用:
-
全局守卫:
beforeEach
和afterEach
。 -
独享路由守卫:
beforeEnter
。 -
组件内路由守卫:
beforeRouteEnter
、beforeRouteUpdate
和beforeRouteLeave
。
全局守卫
全局守卫就是在路由跳转时,对整个应用内的所有路由进行拦截,然后进行一些操作。
全局守卫分为全局前置守卫
和全局后置守卫
。
全局前置守卫
就是在路由跳转之前进行拦截,全局后置守卫
就是在路由跳转之后进行拦截。
全局前置守卫的使用方法如下:
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();
};
- 页面切换动画:在切换页面时添加过渡动画效果,以提升用户体验。可以在
beforeRouteEnter
和beforeRouteLeave
组件内守卫中设置过渡动画的相关逻辑。
const Qux = {
beforeRouteEnter(to, from, next) {
// 在进入组件之前设置初始过渡状态
this.transitionName = 'slide-in';
next();
},
beforeRouteLeave(to, from, next) {
// 在离开组件之前设置过渡状态
this.transitionName = 'slide-out';
next();
},
};