路由守卫?这是个什么东西,听着如此高级。
路由守卫是什么
许多网站的详情页需要我们登录了才可以访问,这就是路由守卫。顾名思义,将没有登录授权的我们守在外面不让进,想进去得先登录账号。这样的功能是不是很常见,当然并不仅仅有这一种情况,很多场景都需要路由守卫,所以了解并掌握它对于学习前端的我们是非常重要的。
路由守卫的几种类型
1. 全局守卫 :在任何路由跳转前或后执行,如beforeEach
和afterEach
。
2. 独享守卫 :只在某个特定路由的进入和离开时执行,如beforeEnter
。
3. 组件内的守卫 :在组件级别的导航守卫,如beforeRouteEnter
和beforeRouteUpdate
现在我们来一一介绍:
全局守卫:
beforeEach
(全局前置守卫)是最常用的路由守卫类型,就是来满足未登录就访问不了的情况,它会带上三个参数,分别是:to 、 from 、 next ,想必大家看到这几个单词就大概猜出它们分别的作用了,说再多都不如上个例子:
js
// 全局的路由守卫
const whitePath = ['/login', '/register', '/noteClass']
router.beforeEach((to, from, next) => {
if (!whitePath.includes(to.path)) { // 需要校验登录权限
// 判断浏览器本地有无userInfo
if (!localStorage.getItem('userInfo')) { // 没登录
router.push('/login')
return
}
next()
return
}
next()
})
当每次路由发生变化时,这个函数都将执行,其中三个参数的含义是:
-
to
: 将去向的路由 -
from
: 当前的路由 -
next
: 放行,允许到达 to 这个路由,也就是即将去的路由
这下是不是豁然开朗了,这段代码在最开始定义了一个白名单,其中包含 login
(登录页面)、register
(注册页面)、noteClass
(主页),不难看出,这三个页面都是我们不需要登录就可以访问的。
下一步则开始设计我们的路由守卫函数:首先,判断 to 的 path (即将去往的路由)是否在白名单中,如果不在,直接走到第13行的 next() 放行,反之继续判断用户是否已经登录,通过判断 localStorage
中是否有用户的登录信息来检测用户是否登录,倘若有值,则直接走到第10行的 next() 放行,反之走 if 中的 router.push('/login')
语句,即跳到登录页。这场景是不是非常熟悉,当然,大部分场景是弹出一个登录框,与之相似。
localStorage
:浏览器的小数据库,当用户登录时会将登录信息以key value的形式存在里面。
独享守卫
与全局守卫的写法一模一样,只不过放的位置不一样。独享守卫,顾名思义,由一个路由独享,所以它是写在路由里面的。就像这样:
js
const routes = [
{ path: '/', component: Home },
{
path: '/profile',
component: Profile,
beforeEnter: (to, from, next) => {
// 假设我们有一个 isUserLoggedIn 方法来检查用户是否登录
const isLoggedIn = isUserLoggedIn();
if (isLoggedIn) {
// 如果用户已登录,允许访问
next();
} else {
// 如果未登录,重定向到登录页面
next('/login');
}
}
},
{ path: '/login', component: Login }
];
直接写在路由里面,由该路由独享这个守卫
组件内的守卫
beforeRouteEnter
和 beforeRouteUpdate
是提供给组件的守卫,直接写在组件中,它们分别在不同的时机触发,用于在导航过程中执行特定的操作。
举例:
假设我们有一个用户详情页面,页面上需要显示用户的个人信息,这些信息需要从服务器动态加载。同时,当用户在页面上修改某些信息并保存后,我们希望再次访问该页面时能够看到最新的信息,而不是缓存中的旧数据。
beforeRouteEnter
beforeRouteEnter
守卫在组件被创建之前被调用,也就是说在组件实例化之前,你就可以开始加载数据。这在数据加载完之后才渲染组件的情况下非常有用,因为这样可以避免组件先渲染出空白界面,然后数据加载完成后才填充内容。
js
// UserDetail.vue
<template>
<div>
<h1>User Detail</h1>
<p>Name: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
user: {}
};
},
beforeRouteEnter(to, from, next) {
axios.get(`/api/users/${to.params.userId}`)
.then(response => {
// 由于 beforeRouteEnter 守卫无法访问 this,所以需要使用 next 回调函数传递结果
next(vm => {
vm.user = response.data;
});
});
}
};
</script>
beforeRouteUpdate
beforeRouteUpdate
则是在组件实例已经创建并且渲染过之后,但在路由参数或查询变化导致的重新渲染之前被调用。这使得你可以在参数变化时执行一些额外的逻辑,例如重新加载数据。
js
// UserDetail.vue
<template>
<!-- 同上 -->
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
user: {}
};
},
beforeRouteEnter(to, from, next) {
// 同上
},
beforeRouteUpdate(to, from, next) {
if (to.params.userId !== from.params.userId) {
// 如果用户 ID 发生变化,重新加载数据
axios.get(`/api/users/${to.params.userId}`)
.then(response => {
this.user = response.data;
next(); // 重要!必须调用 next() 来完成导航
})
.catch(error => {
console.error('Failed to fetch user:', error);
next(); // 即使出现错误,也要调用 next() 来完成导航
});
} else {
next();
}
}
};
</script>
在这个例子中,beforeRouteEnter
负责在首次进入页面时加载用户数据,而 beforeRouteUpdate
则确保当用户 ID 参数发生变化时,数据能够被重新加载。这样,无论何时用户通过导航重新访问该页面,都能看到最新的数据。
需要注意的地方
- 保持守卫逻辑简洁 - 避免在守卫中编写过于复杂的业务逻辑,以防影响性能。
- 使用async/await - 当你需要异步操作时,使用
async/await
可以让守卫的代码更清晰易读。 - 避免无限循环 - 确保在守卫中正确调用
next()
函数,防止死循环。
结语
你如果不想用户还没登录就进入"我的主页"的话,记得用路由守卫。