Vue3路由守卫无法访问Pinia用户状态,需恢复Pinia状态
Vue3路由守卫+Pinia权限校验解决方案
问题根源
你遇到的情况本质是两个问题叠加:
- Pinia默认将状态存在内存中,页面刷新或新开标签页后状态会重置为初始值;
- 若路由守卫注册时机早于Pinia实例挂载,首次执行时无法获取到正确的用户状态。
具体解决方案
1. 实现Pinia状态持久化
将用户角色、JWT token等关键信息同步到localStorage(或sessionStorage),确保页面刷新后状态能恢复。修改你的用户store:
// stores/user.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ role: localStorage.getItem('userRole') || null, token: localStorage.getItem('token') || null }), actions: { // 登录后调用此方法存储用户信息 setUserAuth(role, token) { this.role = role this.token = token localStorage.setItem('userRole', role) localStorage.setItem('token', token) }, // 登出时清空状态 clearUserAuth() { this.role = null this.token = null localStorage.removeItem('userRole') localStorage.removeItem('token') } } })
2. 调整Pinia与路由的加载顺序
确保在注册路由之前先挂载Pinia实例,避免路由守卫初始化时Pinia未就绪。修改main.js:
// main.js import { createApp } from 'vue' import { createPinia } from 'pinia' import router from './router' import App from './App.vue' const app = createApp(App) const pinia = createPinia() // 先挂载Pinia,再挂载路由 app.use(pinia) app.use(router) app.mount('#app')
3. 完善路由守卫逻辑
在路由守卫中,先确保能获取到有效的Pinia实例,再校验用户权限。同时处理本地存储与store状态不同步的极端情况:
// router/index.js import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/stores/user' import { getActivePinia } from 'pinia' const routes = [ // 示例路由,需要权限的页面通过meta.roles指定允许的角色 { path: '/admin', component: () => import('../views/Admin.vue'), meta: { roles: ['admin'] } }, { path: '/user', component: () => import('../views/User.vue'), meta: { roles: ['user', 'admin'] } }, { path: '/login', component: () => import('../views/Login.vue') }, { path: '/403', component: () => import('../views/403.vue') } ] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }) router.beforeEach((to, from, next) => { // 确保Pinia实例已激活 const pinia = getActivePinia() if (!pinia) { return next('/login') } const userStore = useUserStore(pinia) // 同步本地存储到store(避免store初始化时未读取到的情况) if (!userStore.role && localStorage.getItem('userRole')) { userStore.role = localStorage.getItem('userRole') userStore.token = localStorage.getItem('token') } // 登录页直接放行 if (to.path === '/login') { return next() } // 未登录/无角色,跳转登录页 if (!userStore.token || !userStore.role) { return next('/login') } // 权限校验:检查当前角色是否在路由允许的列表中 if (to.meta.roles && !to.meta.roles.includes(userStore.role)) { return next('/403') // 无权限跳转403页面 } next() }) export default router
4. 登录逻辑配合更新
登录成功后调用store的setUserAuth方法,确保状态同步到store和本地存储:
// Login.vue import { useUserStore } from '@/stores/user' import { useRouter } from 'vue-router' export default { setup() { const userStore = useUserStore() const router = useRouter() const handleLogin = async () => { // 调用登录接口获取token和角色 const loginRes = await yourLoginApi(/* 用户名、密码参数 */) const { token, role } = loginRes.data // 存储用户权限信息 userStore.setUserAuth(role, token) // 跳转到首页或目标页面 await router.push('/') } return { handleLogin } } }
内容的提问来源于stack exchange,提问作者sotizerie
相关产品推荐
相关产品推荐

