来看看路由守卫
引言
大家好!今天我们要聊的是Vue 3中的一个非常重要的特性——路由守卫。如果你正在开发一个中大型的Vue应用,那么路由守卫是你绕不开的一个话题。无论是控制页面跳转的逻辑、执行一些数据预加载,还是对用户的权限进行验证,路由守卫都能帮助你更好地管理应用的状态和用户体验。
在我们深入探讨之前,先来快速总结一下为什么路由守卫如此重要:
- 控制导航:通过路由守卫,我们可以控制用户在不同页面间的导航行为。
- 数据预取:在进入某个页面前,可以提前获取数据并准备好状态。
- 权限验证:在用户访问某些受保护的资源前进行身份验证。
假设我们正在构建一个博客系统,用户可以注册、登录,并且只有登录的用户才能查看和编辑他们的个人资料。此外,只有管理员才能访问管理面板。接下来,让我们通过这样一个常见的情况来深入了解路由守卫的各种用法吧。
- 登录页面 (
/login
) - 用户登录。 - 首页 (
/
) - 展示最新文章。 - 用户个人资料 (
/profile
) - 查看和编辑个人资料。 - 管理面板 (
/admin
) - 只有管理员可以访问。
一、认识路由守卫
首先,我们需要一个基本的Vue项目,并安装Vue Router。假设已经有了一个Vue 3项目,现在我们来添加路由功能。
npm install vue-router@next
接着,在项目的src
目录下创建一个名为router
的文件夹,并在其中新建index.js
文件。我们将在这里设置基本的路由配置:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/profile',
name: 'Profile',
component: () => import('../views/Profile.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/admin',
name: 'AdminPanel',
component: () => import('../views/AdminPanel.vue'),
meta: {
requiresAdmin: true
}
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
}
]
在上面,我们定义了两个简单的路由,主页和关于我们页面,步骤大家应该都轻车熟路了。注意懒加载以及meta
字段,利用 meta 来判断是否需要登录来访问这个页面。利用接下来,就让我们添加一些路由守卫。
二、全局路由守卫
全局路由守卫是一种在路由改变之前运行的函数。我们可以利用这些守卫来做一些检查或者处理,比如来检查用户的登录状态以及是否有足够的权限访问某些页面。
1. 设计仓库
搞清楚不同身份的不同权限,设计出所需要的变量以及方法来控制权限的不同。
import { createStore } from 'vuex'
const store = createStore({
state: () => ({
user: null, // 存储当前用户的信息
isLoggedIn: false, // 用户是否登录
isAdmin: false, // 用户是否是管理员
}),
getters: {
isLoggedIn: (state) => state.isLoggedIn,
isAdmin: (state) => state.isAdmin,
currentUser: (state) => state.user,
},
mutations: {
SET_USER(state, user) {
state.user = user
state.isLoggedIn = !!user
state.isAdmin = !!user && user.role === 'admin'
},
LOGOUT(state) {
state.user = null
state.isLoggedIn = false
state.isAdmin = false
},
},
actions: {
login({ commit }, user) {
// 模拟登录逻辑
const success = true; // 假设登录总是成功
if (success) {
commit('SET_USER', user)
}
},
logout({ commit }) {
commit('LOGOUT')
},
},
})
export default store
State
user
: 用于存储当前用户的详细信息,如用户名、角色等。isLoggedIn
: 表示用户是否已登录。isAdmin
: 表示用户是否是管理员。
Getters
isLoggedIn
: 返回isLoggedIn
的状态。isAdmin
: 返回isAdmin
的状态。currentUser
: 返回当前用户的详细信息。
Mutations
SET_USER
: 用于设置或清除用户信息,并更新isLoggedIn
和isAdmin
的状态。LOGOUT
: 用于清除用户信息,并将isLoggedIn
和isAdmin
设置为false
。
Actions
login
: 模拟登录逻辑,通过提交 mutation 更新用户状态。logout
: 提交 mutation 来清除用户信息。
2. 全局前置守卫
全局前置守卫会在每次路由切换前触发。我们可以用它来判断用户是否已经登录,如果没有登录则重定向到登录页。
在src/router/index.js
中添加以下代码:
import { store } from '../store' // 引用一个store用于管理用户状态
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// 需要登录才能访问
if (!store.state.isLoggedIn) {
next({ name: 'Login', query: { redirect: to.fullPath } })
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresAdmin)) {
// 需要是管理员才能访问
if (!store.state.isAdmin) {
next({ name: 'Home' })
} else {
next()
}
} else {
next(); // 确保一定要调用 next()
}
})
-
router.beforeEach
是一个全局前置守卫,它会在每个路由跳转之前被调用。 -
如果目标路由(
to
)的元数据(meta
)包含requiresAuth
属性,则该路由需要用户登录才能访问。 -
如果目标路由的元数据包含
requiresAdmin
属性,则该路由只允许管理员访问。 -
如果用户未登录但尝试访问需要登录的路由,将会被重定向到登录页面,并且登录页面会接收到一个查询参数
redirect
,用来记录用户原本想要访问的路径。 -
如果用户不是管理员但尝试访问需要管理员权限的路由,将会被重定向到首页。
3. 全局解析守卫
我们还可以添加一个全局解析守卫,例如用于预加载数据:
router.beforeResolve((to, from, next) => {
// 在这里可以预加载数据
next()
})
三、局部路由守卫
局部路由守卫允许我们在组件内部定义路由守卫。这对于特定组件的数据加载或条件渲染特别有用。
1. 组件内的守卫
在我们的组件内,可以定义beforeRouteEnter
和beforeRouteUpdate
这样的守卫来控制组件的状态。在Profile.vue
中,我们可以添加一个守卫来检查用户是否已经登录。
export default {
beforeRouteEnter(to, from, next) {
// 在组件被激活之前调用
if (!store.state.isLoggedIn) {
next('/login')
} else {
next(vm => {
vm.fetchProfileData() // 假设这是一个异步加载用户数据的方法
});
}
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
if (to.params.userId !== from.params.userId) {
this.fetchProfileData()
}
next()
},
methods: {
fetchProfileData() {
// 获取用户数据
}
}
}
-
beforeRouteEnter
守卫:-
这个守卫在渲染这个组件的路由被激活之前调用。
-
它检查用户是否已经登录 (
store.state.isLoggedIn
)。如果没有登录,则重定向用户到登录页面 ('/login'
)。如果已登录,则等待组件创建完成之后调用fetchProfileData()
方法来加载用户数据。
-
-
beforeRouteUpdate
守卫:-
当当前路由改变但该组件实例被复用时会调用这个守卫。
-
它检查路由参数中的
userId
是否发生了变化 (to.params.userId !== from.params.userId
)。如果userId
发生了变化,则调用fetchProfileData()
方法来更新用户数据。
-
-
fetchProfileData
方法:- 这个方法负责获取用户个人资料数据,通常是通过从 API 或者数据存储中获取。
- 具体实现细节没有给出,但通常它会发起一个异步请求,并将新数据更新到组件的状态中。
需要注意的是,beforeRouteEnter
守卫无法直接访问 this
(Vue 实例),因为此时组件还没有被创建。这就是为什么 next
被调用时带有一个回调函数,这个回调函数接收 Vue 实例 (vm
) 作为参数。beforeRouteUpdate
守卫可以直接访问 this
,因为此时组件已经被创建并处于活动状态。
四、导航守卫
除了全局和局部守卫之外,Vue Router还提供了navigation guards
,这是一些更细粒度的守卫,它们允许你在组件实例中拦截导航。
1. 使用 beforeRouteLeave
最后,我们可以在Profile.vue
中添加一个beforeRouteLeave
守卫,以确保在用户离开页面前执行一些操作,比如保存表单数据。
export default {
// ...之前的代码
beforeRouteLeave(to, from, next) {
if (this.formChanged) {
// 如果表单有更改,询问用户是否保存
const answer = window.confirm('确定不保存更改吗?')
if (answer) {
next();
} else {
next(false)
}
} else {
next()
}
},
data() {
return {
formChanged: false,
}
},
methods: {
// ...之前的代码
onFormChange() {
this.formChanged = true
},
},
}
首先检查 formChanged
数据属性是否为 true
,表示表单数据已发生更改。
如果 formChanged
为 true
,则弹出一个确认对话框询问用户是否继续离开而不保存更改。如果用户点击“确定”(answer
为 true
),则调用 next()
函数允许导航继续。
如果用户点击“取消”,则调用 next(false)
阻止导航。如果 formChanged
为 false
,则直接调用 next()
允许导航继续。
这样我们确保在离开页面前保存了表单数据,可以防止用户在没有保存的情况下丢失数据。
总结
通过今天的介绍,大家应该对Vue 3中的路由守卫有了全面的认识。路由守卫是Vue Router中一项强大的特性,它可以帮助你控制应用的导航流程、数据加载以及用户交互等,我们最后再巩固一下基本认识。
- 全局守卫:适合全局性的逻辑处理,如权限验证。
- 局部守卫:用于组件内部,可以用来加载数据或做条件渲染。
- 导航守卫:可以用来做一些清理工作,如保存表单数据。
希望这篇文章能帮助你更好地理解和运用路由守卫。一起加油!
转载自:https://juejin.cn/post/7395413975976198178