You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3路由守卫无法访问Pinia用户状态,需恢复Pinia状态

Vue3路由守卫+Pinia权限校验解决方案

问题根源

你遇到的情况本质是两个问题叠加:

  1. Pinia默认将状态存在内存中,页面刷新或新开标签页后状态会重置为初始值;
  2. 若路由守卫注册时机早于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 05:22:49