Pinia 状态管理:模块化、持久化与“权限联动”落地

Pinia 状态管理:模块化、持久化与"权限联动"落地

很多项目上 Pinia 不难用,但容易用成两种极端:

  • 全部状态都塞进 store,组件越来越"胖"
  • store 只存 token,其它状态各自维护,协作成本变高

这篇按"项目落地"的方式讲 Pinia:

  • store 如何分模块(领域边界)
  • state/ getter / action 如何设计(可维护)
  • 持久化存什么、不存什么(安全与一致性)
  • 与 Axios 拦截器、权限路由怎么联动成闭环

目标是:写出来的 store 不仅能跑,还能在项目迭代中长期稳定。


1. Pinia 在项目里解决什么问题

  • 跨页面共享状态:登录态、用户信息、字典、主题配置
  • 减少 props/emit 链路:避免层层传递
  • 统一副作用入口:把"请求 + 缓存 + 失效"放在 store action

2. store 怎么分模块(建议的边界)

按"领域"拆,而不是按"页面"拆:

  • useUserStore:token、用户信息、角色/权限、登出
  • useAppStore:主题/布局、侧边栏折叠、全局 loading
  • useDictStore:字典缓存(下拉框、枚举)

避免:

  • 为每个页面新建一个 store(复用差、维护重)

3. 选项式 vs 组合式:先统一团队写法

Pinia 支持两种写法:

  • 选项式(Options Store):更像 Vuex,结构清晰
  • 组合式(Setup Store):更贴近 Composition API,可自由组合逻辑

建议:团队里统一一种写法,避免同一项目两套风格混用。


4. 一个实用的 user store 形态(建议做成"登录态权威源")

ts 复制代码
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: null as null | { id: number; name: string; role: string },
  }),
  getters: {
    isLogin: (s) => !!s.token,
    role: (s) => s.userInfo?.role,
  },
  actions: {
    setToken(token: string) {
      this.token = token
    },
    setUserInfo(info: any) {
      this.userInfo = info
    },
    async logout() {
      this.token = ''
      this.userInfo = null
    },
  },
})

建议:

  • 让"读状态"都从 getters 出口走(组件依赖更清晰)
  • action 负责副作用(请求、缓存、失效)

5. 持久化:token/用户信息怎么存更稳

常见做法:

  • 仅持久化 token
  • userInfo 在刷新后重新拉取(更可信、更安全)

如果你希望持久化(例如减少首屏请求),至少做到:

  • token 过期后要能自动清理
  • 角色变化后要能"强制刷新权限"

(持久化插件的接入方式很多,这里核心是原则,不强调具体库。)

工程建议(更稳):

  • 只持久化 token
  • userInfo 在刷新后重新拉取(避免角色变化、权限变更导致前端缓存"过期")
  • 登出时清理 token 并重置所有权限相关 store

6. Pinia 和 Axios 拦截器联动

典型目标:

  • 请求头自动携带 token
  • 401 统一踢下线/跳登录

伪代码示意:

js 复制代码
axios.interceptors.request.use((config) => {
  const userStore = useUserStore()
  if (userStore.token) config.headers.Authorization = userStore.token
  return config
})

axios.interceptors.response.use(
  (res) => res,
  (err) => {
    if (err.response?.status === 401) {
      const userStore = useUserStore()
      userStore.logout()
      router.replace('/login')
    }
    return Promise.reject(err)
  }
)

关键点:

  • 让"登录态失效"的处理在一个地方收敛
  • store 只做状态与动作,路由跳转由响应拦截器或路由守卫兜底

7. Pinia 和权限路由联动(动态菜单/路由)

常见需求:

  • 不同角色展示不同菜单
  • 路由守卫按角色放行/拒绝

落地方式:

  • userStore.userInfo.role 作为权限源
  • permissionStore.routes 作为可访问路由表
  • 登录后拉取用户信息 -> 生成动态路由 -> router.addRoute

建议:

  • 动态路由生成要可重入(刷新、重新登录都能跑一遍)
  • 退出登录要清理动态路由与缓存菜单

工程落地的关键点:

  • userStore 只负责"登录态、用户信息、权限源数据"
  • permissionStore 负责"根据权限源生成可访问路由/菜单"
  • 路由守卫负责"什么时候生成、什么时候注入、什么时候重建"

8. 常见坑(反模式清单)

  • 组件外直接解构 store:会丢响应
  • 把接口响应原样塞进 store:字段不稳定,后续很难维护
  • 登录态与权限不同步:只持久化 token,没刷新 userInfo 导致"菜单错乱"

再补两个非常常见的坑:

  • store 里放 UI 临时状态(例如某个弹窗开关):会导致 store 膨胀、依赖混乱
  • action 里直接 return 后端原始结构:页面不得不写兼容逻辑,违背"契约"

9. 总结

  • Pinia 的价值在"状态与副作用统一收敛"
  • store 以领域拆分,不要按页面拆
  • token 持久化,用户信息建议刷新后重新拉取
  • 结合 Axios 拦截器与路由守卫,实现登录态与权限的闭环
相关推荐
毛骗导演2 小时前
@tencent-weixin/openclaw-weixin 源码ContextToken 持久化改造:实现微信自定义消息发送能力
前端·架构
SuperEugene2 小时前
TypeScript+Vue 实战:告别 any 滥用,统一接口 / Props / 表单类型,实现类型安全|编码语法规范篇
开发语言·前端·javascript·vue.js·安全·typescript
luom01022 小时前
SpringBoot - Cookie & Session 用户登录及登录状态保持功能实现
java·spring boot·后端
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 骨科术后营养餐推荐系统为例,包含答辩的问题和答案
java
丶小鱼丶2 小时前
数据结构和算法之【栈】
java·数据结构
我是永恒2 小时前
上架一个跨境工具导航网站
前端
电子羊3 小时前
Spec 编程工作流文档
前端
希望永不加班3 小时前
SpringBoot 核心配置文件:application.yml 与 application.properties
java·spring boot·后端·spring
GISer_Jing3 小时前
从CLI到GUI桌面应用——前端工程化进阶之路
前端·人工智能·aigc·交互