❌ 第一套:按钮级权限直接写死在模板里
当时我们的写法是这样的:
<!-- 用户管理页 -->
<el-button v-if="authList.includes('user:add')">添加用户</el-button>
接口返回的是一个权限数组:
["user:add", "user:delete", "user:list"]
然后整个项目几十个地方都这么判断。
结果:
-
不能重用,每个组件都判断一次;
-
权限粒度变更就全崩,比如从
user:add改成user:add_user; -
后端权限更新后,前端要全局搜索权限 key 改代码;
「典型的“写起来爽,维护时哭”方案。」
❌ 第二套:用 router.meta.permission 统一控制,结果太抽象
重构后我们尝试统一控制页面级权限:
// router.ts
{
path: '/user',
component: User,
meta: {
permission: 'user:list'
}
}
再通过导航守卫:
router.beforeEach((to, from, next) => {
const p = to.meta.permission
if (p && !authList.includes(p)) {
return next('/403')
}
next()
})
这个方案页面级权限是解决了,但「组件级 / 按钮级 / 表单字段级」全都失效了。
而且你会发现,大量页面是“同路由但不同内容区域权限不同”,导致这种 meta.permission 方案显得太粗暴。
❌ 第三套:封装权限组件,结果被吐槽“反人类”
当时我们团队有人设计了一个组件:
<Permission code="user:add">
<el-button>添加用户</el-button>
</Permission>
这个组件内部逻辑是:
const slots = useSlots()
if (!authList.includes(props.code)) return null
return slots.default()
结果:
-
逻辑上看似没问题,但使用非常反直觉;
-
特别是嵌套多个组件时,调试麻烦,断点打不进真实组件;
-
TypeScript 报类型错误,编辑器无法识别 slot 类型;
-
更麻烦的是,权限失效的时候,组件不会渲染,开发环境都看不到是为什么!
最终方案:hook + 指令 + 路由统一层级设计
我们最后把权限体系重构为 3 层:
🔹1. 接口统一管理权限 key → 后端返回精简列表(扁平权限)
export type AuthCode =
| 'user:add'
| 'user:delete'
| 'user:edit'
| 'order:export'
| 'dashboard:view'
服务端返回用户权限集,保存在 authStore(Pinia / Vuex / Context)中。
🔹2. 统一 Hook 调用:usePermission(code)
import { useAuthStore } from '@/store/auth'
export function usePermission(code: string): boolean {
const store = useAuthStore()
return store.permissionList.includes(code)
}
用法:
<el-button v-if="usePermission('user:add')">添加用户</el-button>
这才是真正组件内部逻辑干净、容易复用、TS 支持的方案。
🔹3. 封装一个 v-permission 指令(可选)
app.directive('permission', {
mounted(el, binding) {
const authList = getUserPermissions() // 从全局 store 获取
if (!authList.includes(binding.value)) {
el.remove()
}
}
})
模板中使用:
<el-button v-permission="'order:export'">导出订单</el-button>
适合动态组件、render 生成的按钮,「不适合复杂嵌套逻辑」,但实际项目中效果拔群。
🧪 页面级权限怎么做?
不再用 router.meta,而是把每个路由页封装为权限包裹组件:
<template>
<PermissionView code="dashboard:view">
<Dashboard />
</PermissionView>
</template>
权限组件内部处理:
-
没权限 → 自动跳转 403
-
有权限 → 渲染内容
这样即使权限接口变了,组件逻辑也统一保留,「避免页面空白或者闪跳」。
权限这事,不是实现难,而是维护难。
「最核心的不是你怎么控制显示,而是权限 key 的一致性、复用性、分层能力。」
最终我们稳定版本满足了:
-
页面、按钮、字段统一接入权限
-
新增权限点只需要改枚举,不需要大改
-
新人接手也能一眼看懂逻辑,能调试
前端权限系统设计的最佳实践
1197

被折叠的 条评论
为什么被折叠?



