文章目录
简介
Vue Router 提供了路由钩子(Route Hooks),允许在路由发生变化时执行一些操作。
这些钩子可以在路由进入、离开等关键时刻触发,从而实现一些特定的功能,比如页面权限控制、数据预加载等。
路由守卫简单来讲就是监听页面进入,修改,和离开的功能。
每个守卫接受三个参数:
- to:即将要进入的路由对象
- from:当前导航正要离开的路由
- next:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
关于 next :
next()
:进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false)
:中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。next('/')
或next({ path: '/' })
:跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。next(error)
:(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
一、路由钩子分为三类⚡️
-
全局钩子:
beforeEach
、beforeResolve
、afterEach
-
路由独享钩子:
beforeEnter
-
组件内钩子:
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
1. 全局钩子
全局钩子对整个路由实例都有效,它们主要有三个:beforeEach
、beforeResolve
和 afterEach
。
-
beforeEach:全局前置守卫,在路由跳转前触发
例如判断是否登录了,没登录就跳转到登录页 -
beforeResolve:全局解析守卫(2.5.0+)在路由解析完成后(在 beforeRouteEnter 之后)被调用。
这个守卫非常适合用于在导航到新的路由之前预先获取数据或执行其他逻辑,特别是当这些数据需要在组件渲染之前就已经准备好的时候。 -
afterEach:全局后置钩子,在路由跳转后触发
通常用于一些清理工作或者页面跳转后的通知。
const router = new VueRouter({ /* ... */ });
router.beforeEach((to, from, next) => {
// 逻辑判断...
next() // 确保要调用 next() 方法,否则路由不会跳转
})
router.beforeResolve((to, from, next) => {
// 假设需要预先获取一些数据,比如用户的权限信息
next(); // 确保调用 next() 来继续导航
});
router.afterEach((to, from) => {
// 跳转之后滚动条回到顶部
window.scrollTo(0,0);
})
.
2. 路由独享钩子
如果不想全局配置守卫的话,可以为某些路由单独配置 beforeEnter
钩子,它只会在进入该路由时触发。
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 逻辑判断...
next()
}
}
]
})
.
3. 组件内钩子
在 Vue 组件中,可以使用以下路由钩子:beforeRouteEnter
、beforeRouteUpdate
(2.2+ 新增)、beforeRouteLeave
。
-
beforeRouteEnter:在路由进入该组件的渲染函数之前调用,此时组件实例尚未创建,因此无法访问 this。可以通过 next 的回调函数来访问组件实例。
-
beforeRouteUpdate:当同一个路由重新渲染时调用,例如,对于带有动态参数的路由
/foo/:id
,当:id
发生变化时,该组件会被重新渲染。 -
beforeRouteLeave:在导航离开该组件的对应路由时调用。
Vue 2 (Vue Router 3) 中:
export default {
data() {
return {
// ...
}
},
beforeRouteEnter(to, from, next) {
// 组件尚未创建,无法访问 `this`
next(vm => {
// 访问组件实例 `vm`
})
},
beforeRouteUpdate(to, from, next) {
// 当前路由改变,但组件复用时调用
// 例如,对于带查询参数或动态段的路由 `/foo?a=1` 或者 `/foo/1`,
// 组件实例会被复用,此时这个钩子会被调用
this.someData = 'new data'
next()
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
const answer = window.confirm('你确定要离开吗?')
if (answer) {
next()
} else {
next(false) // 取消导航
}
}
}
Vue 3 (Vue Router 4)中:
组件内钩子的使用方式在 Vue 3 中没有显著变化,但如果使用 Composition API
,可能会更倾向于使用 onBeforeRouteEnter
、onBeforeRouteUpdate
和 onBeforeRouteLeave
函数,这些函数可以与 setup 函数
一起使用。
import { onBeforeRouteEnter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
export default {
setup() {
onBeforeRouteEnter((to, from, next) => {
next(vm => {
// 访问组件实例
});
});
onBeforeRouteUpdate((to, from, next) => {
// ...当前路由改变,但组件复用时调用
next();
});
onBeforeRouteLeave((to, from, next) => {
// ...导航离开该组件的对应路由时调用
next();
};
// ...其他 Composition API 逻辑
}
};
vue-router 3.x | vue-router 4.x |
---|---|
beforeRouteEnter | onBeforeRouteEnter |
beforeRouteUpdate | onBeforeRouteUpdate |
beforeRouteLeave | onBeforeRouteLeave |
二、执行顺序⚡️
全局钩子、路由独享钩子和组件内钩子的执行顺序如下:
-
全局前置守卫 (beforeEach):在路由跳转前被触发。如果此守卫返回 false,则导航将被终止。
-
路由独享的守卫 (beforeEnter):在全局前置守卫之后,进入路由独享的守卫(如果有定义)。这些守卫只在当前路由被匹配时才会被调用。
-
全局解析守卫 (beforeResolve):在 beforeRouteEnter 守卫调用之后,vue-router 会在导航被确认之前调用这个守卫。这意味着在导航被确认之前,所有的组件内守卫和异步路由组件都已被解析。
-
组件内的守卫 (beforeRouteEnter):在渲染该组件的对应路由被确认之前调用。不能获取组件实例 this,因为当守卫执行时,组件实例还没被创建。
-
全局后置钩子 (afterEach):不管导航成功还是失败,这个钩子都会被调用。
-
组件内的守卫 (beforeRouteUpdate):在当前路由改变,但是该组件被复用时调用。
例如,对于带有动态参数的路由/user/:id
,在/user/1
和/user/2
之间跳转的时候,由于会渲染同样的 User 组件,因此这个守卫就会被调用。此时可以访问组件实例 this。 -
组件内的守卫 (beforeRouteLeave):导航离开该组件的对应路由时调用。可以用来阻止一个即将离开的路由。
即:
- 全局
beforeEach
钩子 (进入) - 路由独享
beforeEnter
钩子(进入) - 全局
beforeResolve
钩子(进入) - 组件内
beforeRouteEnter
钩子(进入) - 全局
afterEach
钩子(进入) - 组件内
beforeRouteUpdate
钩子(更新) - 组件内的
beforeRouteLeave
钩子(离开)
三、路由鉴权 ⚡️
在 Vue.js 中,路由鉴权是一个常见的需求,主要用于控制用户对特定路由的访问权限。
以下是一个简单的步骤,用于在 Vue Router 中实现路由鉴权:
1. 定义路由是否需要鉴权
首先,需要在 Vue Router 中定义路由。
可以为每个路由添加一个 meta 字段,用于存储关于该路由的额外信息,例如权限要求。
const routes = [
{
path: '/login',
component: Login,
meta: {
title: '登录',
requiresAuth: false // 不需要权限的路由
}
},
{
path: '/profile/address',
name: 'MyAddress',
component: MyAddress,
meta: {
title: '我的地址',
requireAuth: true // 只要此字段为true,必须做鉴权处理
}
}
// 更多...
];
.
2. 通过全局前置守卫鉴权
使用 Vue Router 的 beforeEach
导航守卫,可以在每个路由被访问之前进行鉴权。
在这个守卫中,可以检查用户的登录状态或权限,并决定是否允许他们访问该路由。
router.beforeEach((to, from, next) => {
let userData = getUserData(); // 获取存储在本地的一些用户信息
if (to.matched.some(res => res.meta.requireAuth)) {
// 如果路由需要权限
if (!userData.token) {
// 如果用户未登录或没有权限
next('/login'); // 重定向到登录页面
} else {
next(); // 允许访问
}
} else {
next(); // 不需要权限的路由,直接访问
}
});
.
3. 完整代码
/router/index.js
var router;
export default router = new Router({
routes: [
{
path: '/profile/address',
name: 'MyAddress',
component: MyAddress,
meta: {
title: '我的地址',
requireAuth: true // 只要此字段为true,必须做鉴权处理
}
}
]
});
let indexScrollTop = 0;
router.beforeEach((to, from, next) => {
// 路由进入下一个路由对象前,判断是否需要登陆
// 在路由meta对象中由个requireAuth字段,只要此字段为true,必须做鉴权处理
if (to.matched.some(res => res.meta.requireAuth)) {
// userData为存储在本地的一些用户信息
let userData = getUserData();
// 如果没有token,则未登录,需要跳转到登录页面
if (userData.token === undefined) {
next({
path: '/login',
query: {
redirect: to.path // 将跳转的路由path作为参数,登录成功后再跳转到该路由
}
});
} else {
// 执行到说明本地存储有用户信息,但是用户信息是否过期还是需要验证一下滴
let overdueTime = userData.overdueTime;
let nowTime = +new Date();
// 登陆过期和未过期
if (nowTime > overdueTime) {
// 登录过期的处理,需要跳转到登录页面
next({
path: '/login',
query: {
redirect: to.path
}
});
} else {
next();
}
}
} else {
next();
}
if (to.path !== '/') {
// 记录现在滚动的位置
indexScrollTop = document.body.scrollTop;
}
document.title = to.meta.title || document.title;
});
router.afterEach(route => {
if (route.path !== '/') {
document.body.scrollTop = 0;
} else {
Vue.nextTick(() => {
// 回到之前滚动位置
document.body.scrollTop = indexScrollTop;
});
}
});
export default router;
导航去登录页面调用的
next
方法里面有个query
对象,携带了目标路由的地址,这是因为在登录成功后我们需要重定向到目标页面。
四、错误处理
-
Vue Router 4(适用于 Vue 3)中:
可以使用
router.onError
方法来注册一个错误处理回调,用来检测和处理可能发生的错误。这个方法允许定义一个函数,该函数会在路由导航过程中发生错误时被调用。onError
并不是一个路由钩子(guard)或生命周期钩子,而是一个错误处理函数,可以注册到 Vue Router 实例上以便在路由导航过程中发生错误时进行处理。以下是一个简单的示例,展示了如何在 Vue Router 4 中使用 onError 方法:
import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ history: createWebHistory(), routes: [ // 路由配置... ], }); router.onError((error) => { // 处理路由导航中发生的错误 console.error('路由导航发生错误:', error); }); export default router;
.
-
Vue Router 3(适用于 Vue 2)中:
在 Vue Router 3 或更早的版本中,没有直接的
onError
方法,但可以通过全局的afterEach
钩子来检测和处理可能发生的错误。例如,如果某个路由守卫中调用了
next(error)
,可以在afterEach
钩子中检测到这个错误并进行处理:router.afterEach((to, from, failure) => { if (failure) { // 处理路由导航中发生的错误 console.error('路由导航发生错误:', failure); } });
afterEach
钩子的第三个参数failure
在 Vue Router 3.1.0 之后的版本中才被引入,用于传递导航失败时的错误信息。总的来说,虽然 Vue Router 3 没有直接命名为
onError
的钩子,但可以通过上述方式在路由导航发生错误时进行捕获和处理。
五、知识点
1. router-link
- 在HTML5 history模式下使用了base选项,所有to属性可以不用写基路径
- 会拦击点击事件,不会重新加载页面.
- router-link默认渲染为a标签,我们可以通过tag属性设置为别的标签(常用的li).
- to属性可以绑定name(命名组件),query(带查询参数)
<router-link :to="{name:'entityList', query:{page: 'Ecp.SystemMessage.List.vdp'}}" tag="li">
</router-link>
.
2. router-view
- router-view: 渲染匹配到的视图组件,router-view匹配到的视图组件里面还可以嵌套自己的router-view.根据嵌套路径(children)来继续渲染.
- router-view可以通过name属性来渲染对应的component下相应的组件
- router-view可以配合transition与keep-alive使用,如果同时使用,里面要使用keep-alive.
- keep-alive缓存router的请求
<transition name="fade" mode="out-in" key="$route.path">
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>