好的,各位观众老爷们,今天咱们来聊聊 Vue Router 里的导航守卫,这玩意儿就像咱们进出皇宫的层层把关,保证安全又可控。别看名字挺唬人,其实理解了它的执行流程,你就能像指挥千军万马一样掌控路由的跳转。
开场白:导航守卫是啥?
导航守卫,简单来说,就是在路由跳转过程中,给你提供的一系列钩子函数。你可以通过这些钩子函数,在路由跳转的不同阶段,做一些事情,比如:
- 权限校验: 判断用户是否有权限访问某个页面。
- 页面统计: 记录用户的访问轨迹。
- 数据预取: 在页面渲染前,先把数据准备好。
- 滚动条位置还原: 记住上次浏览的位置,下次进来时自动滚动到那里。
- 等等等等…
这就像你进饭店,服务员会在门口问你几位,引导你去哪里,点什么菜,吃完结账送你出门一样。导航守卫就是 Vue Router 的服务员,帮你处理路由跳转的各种杂事。
正文:导航守卫的种类和执行顺序
Vue Router 提供了三种全局导航守卫,分别是:
beforeEach
(全局前置守卫): 在每次路由跳转前都会执行。这是你进入饭店大门前的保安,先检查你有没有带刀,穿着是否得体。beforeResolve
(全局解析守卫): 在所有组件内的守卫和异步路由组件解析之后,导航被确认之前调用。可以理解为饭店领班,在你入座前,确认一下你的订单是否正确。afterEach
(全局后置钩子): 在每次路由跳转后都会执行。这是你吃完饭出门时,服务员跟你说声谢谢惠顾。
除了全局导航守卫,还有组件内的守卫和路由独享的守卫。咱们今天重点讲全局的,因为他们最常用,也最能体现导航守卫的核心思想。
执行顺序:beforeEach
-> beforeResolve
-> afterEach
这个顺序很重要,就像你先安检,再点菜,最后出门一样,不能乱来。
详细剖析:每个守卫的作用和用法
1. beforeEach
(全局前置守卫)
这是最重要的一个守卫,也是最常用的。它会在每次路由跳转前执行,你可以用它来做很多事情,比如权限校验。
-
参数:
to
: 即将要进入的目标 路由对象。from
: 当前导航正要离开的路由对象。next
: 一个函数,用来控制路由的跳转。
-
next
函数:next
函数是beforeEach
守卫的核心,它决定了路由是否能够继续跳转。你可以把它想象成一把钥匙,只有拿到这把钥匙,才能打开通往目标页面的大门。next()
: 允许路由继续跳转到目标页面。next(false)
: 中断当前的导航。next('/')
或next({ path: '/' })
: 跳转到不同的路由。next(error)
: 中断导航,并将错误传递给router.onError()
注册过的回调。
-
代码示例:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/', component: { template: '<div>Home Page</div>' } }, { path: '/profile', component: { template: '<div>Profile Page</div>' }, meta: { requiresAuth: true } // 需要登录才能访问 }, { path: '/login', component: { template: '<div>Login Page</div>' } } ] const router = new VueRouter({ routes }) router.beforeEach((to, from, next) => { // 假设有一个 isAuthenticated 函数,用来判断用户是否已登录 const isAuthenticated = () => { // 这里只是一个模拟,实际情况需要根据你的登录逻辑来判断 return localStorage.getItem('token') !== null; }; if (to.meta.requiresAuth) { // 如果目标路由需要登录才能访问 if (isAuthenticated()) { // 如果用户已登录,则允许继续跳转 next(); } else { // 如果用户未登录,则跳转到登录页面 next('/login'); } } else { // 如果目标路由不需要登录,则允许继续跳转 next(); } }) export default router
在这个例子中,我们定义了一个
requiresAuth
的meta
字段,用来标记哪些页面需要登录才能访问。在beforeEach
守卫中,我们判断目标路由是否需要登录,如果需要,则判断用户是否已登录,如果已登录,则允许继续跳转,否则跳转到登录页面。这个例子就像饭店规定,VIP包间需要会员卡才能进入,保安会在门口检查你的会员卡。
2. beforeResolve
(全局解析守卫)
这个守卫用的比较少,但也有它的用处。它会在所有组件内的守卫和异步路由组件解析之后,导航被确认之前调用。
-
作用:
beforeResolve
守卫主要用于在路由被确认之前,执行一些异步操作,比如获取一些数据,或者做一些其他的准备工作。 -
与
beforeEach
的区别:beforeResolve
守卫会在所有组件内的守卫和异步路由组件解析之后执行,这意味着它可以访问到组件实例和异步路由组件。而beforeEach
守卫在这些组件解析之前执行,无法访问到这些信息。 -
代码示例:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/user/:id', component: () => import('./components/User.vue') // 异步组件 } ] const router = new VueRouter({ routes }) router.beforeResolve((to, from, next) => { // 在异步组件解析之后,获取用户信息 import('./api/user') .then(userApi => { return userApi.getUser(to.params.id); }) .then(user => { // 将用户信息存储到路由对象中,方便组件使用 to.meta.user = user; next(); }) .catch(error => { console.error('Failed to fetch user data:', error); next(error); // 将错误传递给 router.onError() }); }); export default router
在这个例子中,我们在
beforeResolve
守卫中,异步加载用户信息,并将用户信息存储到路由对象的meta
字段中。这样,在组件中,就可以通过this.$route.meta.user
来访问用户信息了。这个例子就像饭店领班,在客人入座前,确认一下客人的预定信息,然后把客人的喜好告诉服务员。
3. afterEach
(全局后置钩子)
这个钩子函数会在每次路由跳转后执行,它不会接收 next
函数,因为路由已经跳转完成了,你只能做一些收尾工作。
-
作用:
afterEach
钩子函数主要用于做一些页面统计、日志记录等收尾工作。 -
参数:
to
: 即将要进入的目标 路由对象。from
: 当前导航正要离开的路由对象。
-
代码示例:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/', component: { template: '<div>Home Page</div>' } }, { path: '/profile', component: { template: '<div>Profile Page</div>' } } ] const router = new VueRouter({ routes }) router.afterEach((to, from) => { // 记录页面访问日志 console.log(`Navigated from ${from.path} to ${to.path}`); // 可以使用第三方库进行页面统计,比如 Google Analytics // ga('set', 'page', to.path); // ga('send', 'pageview'); }) export default router
在这个例子中,我们在
afterEach
钩子函数中,记录了页面的访问日志,并使用了 Google Analytics 进行页面统计。这个例子就像饭店服务员,在你出门时,跟你说声谢谢惠顾,并记录你的消费信息。
总结:导航守卫的执行流程
下面用一个表格来总结一下导航守卫的执行流程:
阶段 | 守卫类型 | 作用 | 参数 | next 函数 |
---|---|---|---|---|
路由跳转前 | beforeEach |
在每次路由跳转前执行,可以进行权限校验、数据预取等操作。 | to , from , next |
可用 |
路由解析后 | beforeResolve |
在所有组件内的守卫和异步路由组件解析之后,导航被确认之前调用。 | to , from , next |
可用 |
路由跳转后 | afterEach |
在每次路由跳转后执行,可以进行页面统计、日志记录等操作。 | to , from |
不可用 |
代码示例:一个完整的例子
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
component: { template: '<div>Home Page</div>' }
},
{
path: '/profile',
component: { template: '<div>Profile Page</div>' },
meta: { requiresAuth: true }
},
{
path: '/login',
component: { template: '<div>Login Page</div>' }
},
{
path: '/user/:id',
component: () => import('./components/User.vue') // 异步组件
}
]
const router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
const isAuthenticated = () => {
return localStorage.getItem('token') !== null;
};
if (to.meta.requiresAuth) {
if (isAuthenticated()) {
next();
} else {
next('/login');
}
} else {
next();
}
})
router.beforeResolve((to, from, next) => {
if (to.path.startsWith('/user/')) {
import('./api/user')
.then(userApi => {
return userApi.getUser(to.params.id);
})
.then(user => {
to.meta.user = user;
next();
})
.catch(error => {
console.error('Failed to fetch user data:', error);
next(error);
});
} else {
next();
}
});
router.afterEach((to, from) => {
console.log(`Navigated from ${from.path} to ${to.path}`);
})
export default router
注意事项:
- 异步操作: 在
beforeEach
和beforeResolve
守卫中,可以执行异步操作,但一定要确保next
函数在异步操作完成后调用,否则路由会一直卡在那里。 - 错误处理: 如果在守卫中发生了错误,可以使用
next(error)
将错误传递给router.onError()
注册过的回调。 - 避免死循环: 在守卫中,要避免出现死循环,比如在
beforeEach
守卫中,总是跳转到同一个页面,导致守卫被无限循环调用。 - 组件内的守卫: 除了全局守卫,Vue Router 还提供了组件内的守卫,比如
beforeRouteEnter
,beforeRouteUpdate
,beforeRouteLeave
。这些守卫只能在组件内部使用,可以用来做一些组件相关的操作。 - 路由独享的守卫: 可以在路由配置中直接定义
beforeEnter
守卫,它只对当前路由有效。
总结:
导航守卫是 Vue Router 中非常重要的一个特性,它可以让你在路由跳转的不同阶段,做一些事情,比如权限校验、数据预取、页面统计等。理解了导航守卫的执行流程,你就能更好地控制路由的跳转,提升用户体验。
好了,今天的讲座就到这里。希望大家能够掌握导航守卫的用法,在实际项目中灵活运用,写出更加健壮的 Vue 应用。 散会!