使用 provide/inject 实现权限信息的全局共享,核心就在于“根组件统一注入,任意子组件都能直接获取并使用”。这种方案实现起来并不复杂:在应用根部 provide 一个 reactive 的权限对象,内部包含 list、has 方法等关键能力;子组件根据需要 inject 即可。再配合 usePermission 做一层组合式封装,把菜单权限、路由守卫、指令校验以及本地缓存恢复等流程串联起来,整个 Vue 后台管理系统的权限控制会更加清晰、高效。

在后台管理布局中,如果想把登录用户的权限信息做成全局共享,使用 provide/inject 是一种非常实用且轻量的方案。它的关键逻辑可以概括为一句话:根部注入一次,后续各层组件随处可用。这样既不需要借助 Pinia 或 Vuex 反复中转状态,也无需通过 props 层层传递。真正的重点在于,将权限状态与权限校验能力统一封装进一个响应式对象中,再在应用根组件集中 provide,业务子组件按需 inject 使用即可,从而实现更优雅的 Vue 权限管理。
在根组件或 main.ts 中统一 provide 权限实例
权限数据通常来源于登录后的用户信息接口,例如按钮权限数组、角色标识或权限位图。最佳实践是将这些数据封装为 reactive 响应式对象,并使用 Symbol 作为 key 以避免命名冲突:
- 定义唯一 Symbol:const PERMISSION_KEY = Symbol('permission')
- 创建 reactive 权限对象,包含权限列表、校验方法和刷新能力:例如 list: ref(['user:add', 'order:delete']), has: (key) => list.value.includes(key)
- 在 main.ts 的 createApp 后调用 app.provide(PERMISSION_KEY, permissionState),或在 App.vue 的 setup 中通过 provide(PERMISSION_KEY, permissionState) 统一注入
任意组件中 inject 获取并安全使用
在具体业务组件中,不需要直接 import 权限模块的完整实现,只需通过 inject 获取权限实例。结合 composable 做二次封装,代码会更规范、维护性也更高:
- 基础写法:const perm = inject(PERMISSION_KEY),建议增加非空判断,避免运行时因未注入导致报错
- 推荐封装 usePermission() 组合式函数,统一返回 { has, bits } 等更具语义化的方法,模板和脚本中都能直接解构使用
- 支持响应式联动:has 方法内部读取 ref 或 computed,确保权限变更后,v-if、按钮显隐或自定义指令状态可以自动更新
配合路由与菜单做一致性控制
通过 provide/inject 提供的权限对象并不是独立存在的,它应与动态路由、菜单渲染和页面访问控制保持一致,才能构建完整的后台权限体系:
- 菜单生成时遍历路由配置,使用 has(key) 判断当前用户是否具备显示该菜单项的权限(key 可对应 route.meta.permissionCode)
- 按钮级权限控制可使用 v-if="has('order:export')",也可以配合自定义 v-permission 指令,在指令内部通过 inject 调用权限校验能力
- 路由守卫中同样可以接入权限实例,用于 beforeEnter 或全局守卫中执行更细粒度的页面跳转拦截与访问判断
注意持久化与刷新场景下的同步
页面刷新后,登录用户的权限状态不能直接丢失,因此需要配合本地缓存和接口重新拉取机制,保证权限数据的连续性与准确性:
- 登录成功后,将权限数据写入 localStorage 或 sessionStorage,便于刷新后恢复
- App 启动时优先从缓存恢复权限 state,再触发 refresh 接口进行最新数据同步,避免首屏长时间空白等待
- 需要注意的是,provide 的 reactive 对象本身并不会自动持久化,因此应在初始化阶段手动执行 restore,并通过 watch 或接口更新同步后端权限变更
